From: Stephen S. <sg...@gm...> - 2005-10-05 04:21:22
|
This is a preliminary "SPI protocol" driver for the Cirrus Logic CS8415A S/PDIF decoder chip. This driver demonstrates some but not all of the features of David Brownell's "simple SPI framework" and is intended to demonstrate and test the PXA SSP driver posted previously. ---- snip ---- drivers/spi/cs8415a.c | 561 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/spi/cirrus.h | 200 +++++++++++++++ include/linux/spi/cs8415a.h | 156 ++++++++++++ 3 files changed, 917 insertions(+) --- linux-2.6.12-spi/include/linux/spi/cirrus.h=091969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.12-spi-pxa/include/linux/spi/cirrus.h=092005-10-04 13:05:06.897768000 -0700 @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CIRRUS_H_ +#define CIRRUS_H_ + +#include <linux/list.h> +#include <linux/spi.h> +#include <linux/spinlock.h> +#include <linux/device.h> + +#define READ_CMD 0x21 +#define WRITE_CMD 0x20 +#define TO_MAP(x) ((x & 0x7f) | 0x80) +#define MAX_POOL_NAME_SIZE 64 + +struct cirrus_message_pool { +}; + +struct cirrus_notification { +}; + +struct cirrus_pooled_message { +}; + +inline void _cirrus_setup_async(struct spi_message *message, +=09=09=09=09=09void (*chip_callback)(void *context), +=09=09=09=09=09void (*user_callback)(int length)) +{ +}; + +inline void *__cirrus_pool_alloc(struct cirrus_message_pool *pool) +{ +=09return NULL;=09 +} + +inline void __cirrus_pool_free(struct cirrus_message_pool *pool, void* ptr= ) +{ +} + +inline struct spi_message *_cirrus_alloc_read(struct cirrus_message_pool *pool, +=09=09=09=09=09=09=09u8 address, +=09=09=09=09=09=09=09const char* buffer, +=09=09=09=09=09=09=09size_t length) +{ +=09return NULL; +} + +inline struct spi_message *_cirrus_alloc_write(struct cirrus_message_pool *pool, +=09=09=09=09=09=09=09u8 address, +=09=09=09=09=09=09=09unsigned char* buffer, +=09=09=09=09=09=09=09unsigned int length) +{ +=09return NULL; +} + +inline void _cirrus_free(struct cirrus_message_pool *pool, +=09=09=09=09struct spi_message* message) +{ +} + +inline struct cirrus_message_pool *_cirrus_create_pool(const char* name, +=09=09=09=09=09=09=09size_t pool_size, +=09=09=09=09=09=09=09size_t max_message_size) +{ +=09return NULL; +} + +inline void _cirrus_release_pool(struct cirrus_message_pool *pool) +{ +} + +inline ssize_t _cirrus_read_sync(struct device *dev, u8 address, +=09=09=09=09=09const char *buffer, size_t length) +{ +=09u8 map_buffer[] =3D { WRITE_CMD,=09TO_MAP(address) }; +=09u8 read_cmd_buffer[] =3D { READ_CMD }; +=09struct spi_transfer transfers[3]; +=09struct spi_message message; +=09 +=09transfers[0].tx_buf =3D map_buffer; +=09transfers[0].rx_buf =3D NULL; +=09transfers[0].len =3D ARRAY_SIZE(map_buffer); +=09transfers[0].cs_change =3D 0; +=09transfers[0].delay_usecs =3D 0; + +=09transfers[1].tx_buf =3D read_cmd_buffer; +=09transfers[1].rx_buf =3D NULL; +=09transfers[1].len =3D ARRAY_SIZE(read_cmd_buffer); +=09transfers[1].cs_change =3D 1; +=09transfers[1].delay_usecs =3D 0; +=09 +=09transfers[2].tx_buf =3D NULL; +=09transfers[2].rx_buf =3D (void *)buffer; +=09transfers[2].len =3D length; +=09transfers[2].cs_change =3D 0; +=09transfers[2].delay_usecs =3D 0; + +=09message.transfers =3D transfers; +=09message.n_transfer =3D ARRAY_SIZE(transfers); + +=09spi_sync(to_spi_device(dev), &message); +=09 +=09return message.status < 0 ? message.status : message.actual_length; +} + +inline int _cirrus_write_sync(struct device *dev, u8 address, +=09=09=09=09const unsigned char *buffer, +=09=09=09=09unsigned int length) +{ +=09u8 map_buffer[] =3D { WRITE_CMD, TO_MAP(address) }; +=09struct spi_transfer transfers[2]; +=09struct spi_message message; +=09 +=09transfers[0].tx_buf =3D map_buffer, +=09transfers[0].rx_buf =3D NULL; +=09transfers[0].len =3D ARRAY_SIZE(map_buffer), +=09transfers[0].cs_change =3D 1; +=09transfers[0].delay_usecs =3D 0; +=09 +=09transfers[1].tx_buf =3D (unsigned char *)buffer, +=09transfers[1].rx_buf =3D NULL; +=09transfers[1].len =3D length, +=09transfers[1].cs_change =3D 0; +=09transfers[1].delay_usecs =3D 0; + +=09message.transfers =3D transfers; +=09message.n_transfer =3D ARRAY_SIZE(transfers); + +=09spi_sync(to_spi_device(dev), &message); +=09 +=09return message.status < 0 ? message.status : message.actual_length; +} + +inline int _cirrus_read_register_sync(struct device* dev, u8 address) +{ +=09u8 map_buffer[] =3D { WRITE_CMD, TO_MAP(address) }; +=09u8 read_buffer[] =3D { READ_CMD, 0 }; +=09struct spi_transfer transfers[2]; +=09struct spi_message message; +=09int status; +=09 +=09transfers[0].tx_buf =3D map_buffer, +=09transfers[0].rx_buf =3D NULL; +=09transfers[0].len =3D ARRAY_SIZE(map_buffer), +=09transfers[0].cs_change =3D 1; +=09transfers[0].delay_usecs =3D 0; + +=09transfers[1].tx_buf =3D read_buffer, +=09transfers[1].rx_buf =3D read_buffer, +=09transfers[1].len =3D ARRAY_SIZE(read_buffer), +=09transfers[1].cs_change =3D 0; +=09transfers[1].delay_usecs =3D 0; + +=09message.transfers =3D transfers; +=09message.n_transfer =3D ARRAY_SIZE(transfers); +=09 +=09status =3D spi_sync(to_spi_device(dev), &message); +=09if (status < 0) +=09=09return status; +=09 +=09return message.status < 0 ? message.status : read_buffer[1]; +} + +inline int _cirrus_write_register_sync(struct device* dev, u8 address, u8 = data) +{ +=09u8 write_buffer[] =3D { WRITE_CMD, TO_MAP(address), data }; +=09struct spi_transfer transfers[1]; +=09struct spi_message message; + +=09transfers[0].tx_buf =3D write_buffer, +=09transfers[0].rx_buf =3D NULL; +=09transfers[0].len =3D ARRAY_SIZE(write_buffer), +=09transfers[0].cs_change =3D 0; +=09transfers[0].delay_usecs =3D 0; + +=09message.transfers =3D transfers; +=09message.n_transfer =3D ARRAY_SIZE(transfers); +=09 +=09spi_sync(to_spi_device(dev), &message); +=09 +=09return message.status; +} + +#endif /*CIRRUS_H_*/ --- linux-2.6.12-spi/include/linux/spi/cs8415a.h=091969-12-31 16:00:00.000000000 -0800 +++ linux-2.6.12-spi-pxa/include/linux/spi/cs8415a.h=092005-10-04 14:19:40.422850000 -0700 @@ -0,0 +1,156 @@ +/* cs8415a.h - Definitions for the CS8415A chip + * + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CS8415A_H_ +#define CS8415A_H_ + +#include <linux/list.h> + +#define CS8415A_CTL1 0x01 +#define CS8415A_CTL2 0x02 +#define CS8415A_CSC 0x04 +#define CS8415A_SOF 0x06 +#define CS8415A_I1S 0x07 +#define CS8415A_I2S 0x08 +#define CS8415A_I1MK 0x09 +#define CS8415A_I1MM 0x0a +#define CS8415A_I1ML 0x0b +#define CS8415A_I2MK 0x0c +#define CS8415A_I2MM 0x0d +#define CS8415A_I2ML 0x0e +#define CS8415A_RCS 0x0f +#define CS8415A_RER 0x10 +#define CS8415A_REM 0x11 +#define CS8415A_CSDBC 0x12 +#define CS8415A_UDBC 0x13 +#define CS8415A_QCSB 0x14 +#define CS8415A_ORR 0x1e +#define CS8415A_CUDB 0x20 +#define CS8415A_VER 0x7f + +#define CS8415A_SWCLK (1<<7) +#define CS8415A_MUTESAO (1<<5) +#define CS8415A_INT(x) ((x&3)<<1) +#define CS8415A_HOLD(x) ((x&3)<<5) +#define CS8415A_RMCKF (1<<4) +#define CS8415A_MMR (1<<3) +#define CS8415A_MUX(x) (x&7) +#define CS8415A_RUN (1<<6) +#define CS8415A_SOMS (1<<7) +#define CS8415A_SOSF (1<<6) +#define CS8415A_SORES(x) ((x&3)<<4) +#define CS8415A_SOJUST (1<<3) +#define CS8415A_SODEL (1<<2) +#define CS8415A_SOSPOL (1<<1) +#define CS8415A_SOLRPOL (1) +#define CS8415A_OSLIP (1<<6) +#define CS8415A_DETC (1<<2) +#define CS8415A_RERR (1) +#define CS8415A_DETU (1<<3) +#define CS8415A_QCH (1<<1) +#define CS8415A_AUX(x) ((x&f)<<4) +#define CS8415A_PRO (1<<3) +#define CS8415A_AUDIO (1<<2) +#define CS8415A_COPY (1<<1) +#define CS8415A_ORIG (1) +#define CS8415A_QCRC (1<<6) +#define CS8415A_CCRC (1<<5) +#define CS8415A_UNLOCK (1<<4) +#define CS8415A_V (1<<3) +#define CS8415A_CONF (1<<2) +#define CS8415A_BIP (1<<1) +#define CS8415A_PAR (1) +#define CS8415A_BSEL (1<<5) +#define CS8415A_CBMR (1<<4) +#define CS8415A_DETCI (1<<3) +#define CS8415A_CAM (1<<1) +#define CS8415A_CHS (1) +#define CS8415A_DETUI (1<<1) + +struct cs8415a_platform_data { +=09int enabled; +=09int muted; +=09int channel; +=09void (*mask_interrupt)(void); +=09void (*unmask_interrupt)(void); +=09int (*service_requested)(void); +}; + +struct cs8415a_event { +=09u16 events; +=09void (*event_handler)(u8 event, unsigned char *buffer, unsigned int len= gth); +=09struct list_head event_list; +}; + +extern int cs8415a_get_version(struct device *dev); + +extern int cs8415a_get_enabled(struct device *dev); + +extern int cs8415a_set_enabled(struct device *dev, int value); + +extern int cs8415a_get_muted(struct device *dev); + +extern int cs8415a_set_muted(struct device *dev, int value); + +extern int cs8415a_get_channel(struct device *dev); + +extern int cs8415a_set_channel(struct device *dev, int value); + +extern int cs8415a_is_pll_locked(struct device *dev); + +extern int cs8415a_get_channel_status(struct device *dev); + +extern int cs8415a_read_qch(struct device *dev, unsigned char *buffer); + +extern int cs8415a_read_ubit(struct device *dev, unsigned char *buffer); + +extern int cs8415a_read_cbit(struct device *dev, unsigned char* buffer); + +extern int cs8415a_add_event_handler(struct device *dev, +=09=09=09=09=09struct cs8415a_event *event); +extern int cs8415a_remove_event_handler(struct device *dev, +=09=09=09=09=09=09struct cs8415a_event *event); + +extern int _cs8415a_read_register(struct device *dev, u8 address); + +extern int _cs8415a_write_register(struct device *dev, u8 address, u8 valu= e); + +extern int _cs8415a_read(struct device *dev, u8 address, +=09=09=09=09unsigned char *buffer, unsigned int length); + +extern int _cs8415a_write(struct device *dev, u8 address, +=09=09=09=09unsigned char *buffer, unsigned int length); + +extern int _cs8415a_read_register_async(struct device *dev, u8 address, +=09=09=09=09=09=09void (*done)(int value)); + +extern int _cs8415a_write_register_async(struct device *dev, u8 address, +=09=09=09=09=09=09u8 value, +=09=09=09=09=09=09void (*done)(int status)); +extern int _cs8415a_read_async(struct device *dev, u8 address, +=09=09=09=09=09unsigned char *buffer, +=09=09=09=09=09unsigned int length, +=09=09=09=09=09void (*done)(int length)); + +extern int _cs8415a_write_async(struct device *dev, u8 address, +=09=09=09=09=09unsigned char *buffer, +=09=09=09=09=09unsigned int length, +=09=09=09=09=09void (*done)(int length)); + +#endif /*CS8415A_H_*/ --- linux-2.6.12-spi/drivers/spi/cs8415a.c=091969-12-31 16:00:00.000000000 = -0800 +++ linux-2.6.12-spi-pxa/drivers/spi/cs8415a.c=092005-10-04 14:00:12.279449000 -0700 @@ -0,0 +1,561 @@ +/* + * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/spi.h> +#include <linux/list.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/spi.h> +#include <linux/spi/cs8415a.h> +#include <linux/spi/cirrus.h> + +#include <asm/hardirq.h> +#include <asm/semaphore.h> +#include <asm/arch/streetracer.h> + +MODULE_AUTHOR("Stephen Street"); +MODULE_DESCRIPTION("CS8415A SPI Protocol Driver"); +MODULE_LICENSE("GPL"); + +#define VALID_CS8415A_VERSION (0x41) + +struct cs8415a_driver_data { +=09spinlock_t lock; +=09struct semaphore user_lock; +=09struct cirrus_message_pool *read_async_pool; +=09struct cirrus_message_pool *write_async_pool; +=09struct list_head event_handlers; +=09int chip_version; +=09int enabled; +=09int muted; +=09int channel; +}; + +int _cs8415a_read_register(struct device *dev, u8 address) +{ +=09return _cirrus_read_register_sync(dev, address); +} +EXPORT_SYMBOL(_cs8415a_read_register); + +int _cs8415a_write_register(struct device *dev, u8 address, u8 value) +{ +=09return _cirrus_write_register_sync(dev, address, value); +} +EXPORT_SYMBOL(_cs8415a_write_register); + +int _cs8415a_read(struct device *dev, u8 address, +=09=09=09unsigned char *buffer, unsigned int length) +{ +=09return _cirrus_read_sync(dev, address, buffer, length); +} +EXPORT_SYMBOL(_cs8415a_read); + +int _cs8415a_write(struct device *dev, u8 address, +=09=09=09unsigned char *buffer, unsigned int length) +{ +=09return _cirrus_write_sync(dev, address, buffer, length); +} +EXPORT_SYMBOL(_cs8415a_write); + +int _cs8415a_read_register_async(struct device *dev, u8 address, +=09=09=09=09=09void (*done)(int value)) +{ +=09return -1; +} +EXPORT_SYMBOL(_cs8415a_read_register_async); + +int _cs8415a_write_register_async(struct device *dev, u8 address, +=09=09=09=09=09u8 value, void (*done)(int status)) +{ +=09return -1; +} +EXPORT_SYMBOL(_cs8415a_write_register_async); + +int _cs8415a_read_async(struct device *dev, u8 address, +=09=09=09=09unsigned char *buffer, unsigned int length, +=09=09=09=09void (*done)(int length)) +{ +=09return -1; +} +EXPORT_SYMBOL(_cs8415a_read_async); + +int _cs8415a_write_async(struct device *dev, u8 address, +=09=09=09=09unsigned char *buffer, unsigned int length, +=09=09=09=09void (*done)(int length)) +{ +=09return -1; +} +EXPORT_SYMBOL(_cs8415a_write_async); + +static int cs8415a_reset(struct device *dev) +{ +=09unsigned char clear[6]; +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int status; +=09 +=09memset(clear, 0, sizeof(clear)); +=09 +=09status =3D _cs8415a_write_register(dev, CS8415A_CSC, 0); +=09if (status < 0) +=09=09return status; +=09=09 +=09status =3D _cs8415a_write_register(dev, CS8415A_CTL1, +=09=09=09=09=09=09CS8415A_INT(0) +=09=09=09=09=09=09|CS8415A_MUTESAO); +=09if (status < 0) +=09=09return status; + +=09status =3D _cs8415a_write_register(dev, CS8415A_CTL2, CS8415A_HOLD(1)); +=09if (status < 0) +=09=09return status; + +=09status =3D _cs8415a_write_register(dev, CS8415A_SOF, +=09=09=09=09=09=09CS8415A_SOMS +=09=09=09=09=09=09| CS8415A_SODEL +=09=09=09=09=09=09| CS8415A_SOLRPOL); +=09if (status < 0) +=09=09return status; + +=09status =3D _cs8415a_write(dev, CS8415A_I1MK, clear, sizeof(clear)); +=09if (status < 0) +=09=09return status; + +=09status =3D _cs8415a_read_register(dev, CS8415A_RER); +=09if (status < 0) +=09=09return status; + +=09status =3D _cs8415a_read(dev, CS8415A_I1S, clear, 2); +=09if (status < 0) +=09=09return status; +=09 +=09driver_data->enabled =3D 0; +=09driver_data->channel =3D 0; +=09driver_data->muted =3D 1; +=09 +=09return 0; +} + +static irqreturn_t cs8415a_int(int irq, void *dev_id, struct pt_regs *regs= ) +{ +=09struct cs8415a_driver_data *driver_data; +=09 +=09driver_data =3D dev_get_drvdata((struct device *)dev_id); + +=09spin_lock(&driver_data->lock); +=09 +=09spin_unlock(&driver_data->lock); +=09 +=09return IRQ_HANDLED; +} + +int cs8415a_get_version(struct device *dev) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09 +=09return driver_data->chip_version; +} +EXPORT_SYMBOL(cs8415a_get_version); + +static ssize_t version_show(struct device *dev, char *buf) +{ +=09return sprintf(buf, "0x%02x\n", cs8415a_get_version(dev)); +} + +DEVICE_ATTR(version, 0444, version_show, NULL); + +extern int cs8415a_get_enabled(struct device *dev) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int value; + +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09=09 +=09value =3D driver_data->enabled; +=09 +=09up(&driver_data->user_lock); +=09 +=09return value; +} +EXPORT_SYMBOL(cs8415a_get_enabled); + +extern int cs8415a_set_enabled(struct device *dev, int value) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int status; +=09 +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09=09 +=09status =3D _cs8415a_write_register(dev, CS8415A_CSC, +=09=09=09=09=09=09(u8)(value ? CS8415A_RUN : 0)); +=09if (status =3D=3D 0) +=09=09driver_data->enabled =3D value ? 1 : 0; +=09=09 +=09up(&driver_data->user_lock); + +=09return status; +} +EXPORT_SYMBOL(cs8415a_set_enabled); + +static ssize_t enabled_show(struct device *dev, char *buf) +{ +=09return snprintf(buf, PAGE_SIZE, "%d\n", cs8415a_get_enabled(dev)); +} + +static ssize_t enabled_store(struct device *dev, const char *buf, size_t c= ount) +{ +=09int status; +=09unsigned int value; +=09 +=09status =3D sscanf(buf, "%u", &value); +=09 +=09if (status !=3D 1 || (value !=3D 0 && value !=3D 1)) { +=09=09return -EINVAL; +=09} +=09 +=09cs8415a_set_enabled(dev, value); +=09 +=09return 1; +} + +DEVICE_ATTR(enabled, 0644, enabled_show, enabled_store); + +int cs8415a_get_muted(struct device *dev) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int value; + +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09 +=09value =3D driver_data->muted; +=09 +=09up(&driver_data->user_lock); +=09 +=09return value; +} +EXPORT_SYMBOL(cs8415a_get_muted); + +int cs8415a_set_muted(struct device *dev, int value) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int status; +=09 +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09=09 +=09status =3D _cs8415a_write_register(dev, CS8415A_CTL1, +=09=09=09=09=09=09(u8)(value?CS8415A_MUTESAO:0)); +=09if (status =3D=3D 0) +=09=09driver_data->muted =3D value ? 1 : 0; +=09 +=09up(&driver_data->user_lock); +=09 +=09return status; +} +EXPORT_SYMBOL(cs8415a_set_muted); + +static ssize_t muted_show(struct device *dev, char *buf) +{ +=09return snprintf(buf, PAGE_SIZE, "%d\n", cs8415a_get_muted(dev)); +} + +static ssize_t muted_store(struct device *dev, const char *buf, size_t cou= nt) +{ +=09int status; +=09unsigned int value; +=09 +=09status =3D sscanf(buf, "%u", &value); +=09 +=09if (status !=3D 1 || (value !=3D 0 && value !=3D 1)) { +=09=09return -EINVAL; +=09} +=09 +=09cs8415a_set_muted(dev, value); +=09 +=09return 1; +} +DEVICE_ATTR(muted, 0644, muted_show, muted_store); + +int cs8415a_get_channel(struct device *dev) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int value; +=09 +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09 +=09value =3D driver_data->channel; +=09 +=09up(&driver_data->user_lock); +=09 +=09return value; +} +EXPORT_SYMBOL(cs8415a_get_channel); + +int cs8415a_set_channel(struct device *dev, int value) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int status; +=09 +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09 +=09status =3D _cs8415a_write_register(dev, CS8415A_CTL2, +=09=09=09=09=09=09(u8)(CS8415A_HOLD(1) +=09=09=09=09=09=09=09| CS8415A_MUX(value))); +=09if (status =3D=3D 0) +=09=09driver_data->channel =3D value; +=09=09 +=09up(&driver_data->user_lock); + +=09return status; +} +EXPORT_SYMBOL(cs8415a_set_channel); + +static ssize_t channel_show(struct device *dev, char *buf) +{ +=09return snprintf(buf, PAGE_SIZE, "%d\n", cs8415a_get_channel(dev)); +} + +static ssize_t channel_store(struct device *dev, const char *buf, size_t c= ount) +{ +=09int status; +=09unsigned int value; +=09 +=09status =3D sscanf(buf, "%u", &value); +=09 +=09if (status !=3D 1 || value < 0 || value > 7) { +=09=09return -EINVAL; +=09} +=09 +=09cs8415a_set_channel(dev, value); +=09 +=09return 1; +} +DEVICE_ATTR(channel, 0644, channel_show, channel_store); + +int cs8415a_get_channel_status(struct device *dev) +{ +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09int value; +=09 +=09if (down_interruptible(&driver_data->user_lock)) +=09=09return -ERESTARTSYS; +=09 +=09value =3D _cs8415a_read_register(dev, CS8415A_RCS); +=09 +=09up(&driver_data->user_lock); +=09 +=09return value; +} +static ssize_t channel_status_show(struct device *dev, char *buf) +{ +=09return snprintf(buf, PAGE_SIZE, "0x%01x\n", +=09=09=09=09cs8415a_get_channel_status(dev)); +} +DEVICE_ATTR(channel_status, 0444, channel_status_show, NULL); + +static int cs8415a_spi_probe(struct device *dev) +{ +=09struct spi_device *spi_dev; +=09struct cs8415a_driver_data *driver_data; +=09int status; + +=09/* Allocate driver data */ +=09driver_data =3D kcalloc(1, sizeof(struct cs8415a_driver_data), +=09=09=09=09GFP_KERNEL); +=09if (!driver_data) { +=09=09dev_err(dev, "problem allocating driver memory\n"); +=09=09status =3D -ENOMEM; +=09=09goto out_error; +=09} +=09 +=09spin_lock_init(&driver_data->lock); +=09init_MUTEX(&driver_data->user_lock); +=09INIT_LIST_HEAD(&driver_data->event_handlers); +=09 +=09dev_set_drvdata(dev, driver_data); +=09 +=09/* Initialize the message pool */ +/* +=09driver_data->read_async_pool =3D _cirrus_create_pool(0); +=09driver_data->write_async_pool =3D _cirrus_create_pool(0); +=09if (!driver_data->read_async_pool || !driver_data->write_async_pool) { +=09=09dev_err(dev, "problem creating pools\n"); +=09=09status =3D -ENOMEM; +=09=09goto out_error_memalloc; +=09} +*/ +=09 +=09/* Read and validate version number */ +=09driver_data->chip_version =3D _cs8415a_read_register(dev, CS8415A_VER); +=09if (driver_data->chip_version < 0) { +=09=09dev_err(dev, "problem reading chip version\n"); +=09=09status =3D -ENODEV; +=09=09goto out_error_memalloc; +=09} + +=09if (driver_data->chip_version !=3D VALID_CS8415A_VERSION) { +=09=09dev_err(dev, "problem reading chip version " +=09=09=09=09"found version 0x%02x\n", +=09=09=09=09driver_data->chip_version); +=09=09status =3D -ENODEV; +=09=09goto out_error_memalloc; +=09} +=09 +=09spi_dev =3D to_spi_device(dev); + +=09/* Attach to IRQ */=09 +=09if (spi_dev->irq =3D=3D 0) { +=09=09dev_err(dev, "problem getting irq\n"); +=09=09status =3D -ENODEV; +=09=09goto out_error_memalloc; +=09} + +=09status =3D request_irq(spi_dev->irq, cs8415a_int, +=09=09=09=09SA_SHIRQ, dev->bus_id, dev); +=09if (status < 0) { +=09=09dev_err(dev, "problem requesting IRQ %u\n", spi_dev->irq); +=09=09status =3D -ENODEV; +=09=09goto out_error_memalloc; +=09} +=09 +=09status =3D cs8415a_reset(dev); +=09if (status !=3D 0) { +=09=09dev_err(dev, "problem resetting\n"); +=09=09status =3D -ENODEV; +=09=09goto out_error_irqalloc; +=09} +=09 +=09status =3D device_create_file(dev, &dev_attr_version); +=09if (status < 0) { +=09=09dev_err(dev, "problem creating attribute %s\n", +=09=09=09=09dev_attr_version.attr.name); +=09=09goto out_error_attralloc; +=09} +=09 +=09status =3D device_create_file(dev, &dev_attr_enabled); +=09if (status < 0) { +=09=09dev_err(dev, "problem creating attribute %s\n", +=09=09=09=09dev_attr_enabled.attr.name); +=09=09goto out_error_attralloc; +=09} +=09 +=09status =3D device_create_file(dev, &dev_attr_muted); +=09if (status < 0) { +=09=09dev_err(dev, "problem creating attribute %s\n", +=09=09=09=09dev_attr_muted.attr.name); +=09=09goto out_error_attralloc; +=09} +=09 +=09status =3D device_create_file(dev, &dev_attr_channel); +=09if (status < 0) { +=09=09dev_err(dev, "problem creating attribute %s\n", +=09=09=09=09dev_attr_channel.attr.name); +=09=09goto out_error_attralloc; +=09} + +=09status =3D device_create_file(dev, &dev_attr_channel_status); +=09if (status < 0) { +=09=09dev_err(dev, "problem creating attribute %s\n", +=09=09=09=09dev_attr_channel_status.attr.name); +=09=09goto out_error_attralloc; +=09} + +=09dev_dbg(dev, "found chip version 0x%02x\n", driver_data->chip_version); + +=09return 0; +out_error_attralloc: +=09device_remove_file(dev, &dev_attr_version); +=09device_remove_file(dev, &dev_attr_enabled); +=09device_remove_file(dev, &dev_attr_muted); +=09device_remove_file(dev, &dev_attr_channel); +=09device_remove_file(dev, &dev_attr_channel_status); +=09 +out_error_irqalloc: +=09free_irq(spi_dev->irq, dev); + +out_error_memalloc: +=09if (driver_data->read_async_pool) +=09=09_cirrus_release_pool(driver_data->read_async_pool); +=09=09 +=09if (driver_data->write_async_pool) +=09=09_cirrus_release_pool(driver_data->read_async_pool); +=09 +=09dev_set_drvdata(dev, NULL); + +=09kfree(driver_data); +=09 +out_error: +=09 +=09return status; +} + +static int cs8415a_spi_remove(struct device *dev) +{ +=09struct spi_device *spi_dev =3D to_spi_device(dev); +=09struct cs8415a_driver_data *driver_data =3D dev_get_drvdata(dev); +=09unsigned long flags; + +=09cs8415a_reset(dev); + +=09spin_lock_irqsave(&driver_data->lock, flags); + +=09if (spi_dev->irq !=3D 0) +=09=09free_irq(spi_dev->irq, dev); +=09 +=09spin_unlock_irqrestore(&driver_data->lock, flags); + +=09device_remove_file(dev, &dev_attr_version); +=09device_remove_file(dev, &dev_attr_enabled); +=09device_remove_file(dev, &dev_attr_muted); +=09device_remove_file(dev, &dev_attr_channel); +=09device_remove_file(dev, &dev_attr_channel_status); + +=09_cirrus_release_pool(driver_data->read_async_pool);=09 +=09_cirrus_release_pool(driver_data->write_async_pool);=09 +=09 +=09kfree(driver_data); +=09 +=09return 0; +} + +struct device_driver cs8415a_spi =3D { +=09.name =3D "cs8415a", +=09.bus =3D &spi_bus_type, +=09.owner =3D THIS_MODULE, +=09.probe =3D cs8415a_spi_probe, +=09.remove =3D cs8415a_spi_remove, +}; + +static int __init cs8415a_spi_init(void) +{ +=09return driver_register(&cs8415a_spi); +} +module_init(cs8415a_spi_init); + +static void __exit cs8415a_spi_exit(void) +{ +=09driver_unregister(&cs8415a_spi); +} +module_exit(cs8415a_spi_exit); |