Re: [PATCH v4 2/5] MFD: WL1273 FM Radio: MFD driver for the FM radio.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



--- On Tue, 6/7/10, Mauro Carvalho Chehab <mchehab@xxxxxxxxxx> wrote:

> From: Mauro Carvalho Chehab <mchehab@xxxxxxxxxx>
> Subject: Re: [PATCH v4 2/5] MFD: WL1273 FM Radio: MFD driver for the FM radio.
> To: "Matti J. Aaltonen" <matti.j.aaltonen@xxxxxxxxx>
> Cc: linux-media@xxxxxxxxxxxxxxx, hverkuil@xxxxxxxxx, eduardo.valentin@xxxxxxxxx
> Date: Tuesday, 6 July, 2010, 8:04 AM
> Em 04-06-2010 07:34, Matti J.
> Aaltonen escreveu:
> > This is a parent driver for two child drivers: the
> V4L2 driver and
> > the ALSA codec driver. The MFD part provides the I2C
> communication
> > to the device and a couple of functions that are
> called from both
> > children.

Where can I have a look at the whole code ? As in some local tree, where all codes are put up ?
Basically, we have a V4L2 driver for WL128x (using TTY as transport) and plan to push them soon ...

This would be a nice input ...


> > Signed-off-by: Matti J. Aaltonen <matti.j.aaltonen@xxxxxxxxx>
> > ---
> >  drivers/mfd/Kconfig       
>      |    6 +
> >  drivers/mfd/Makefile       
>     |    2 +
> >  drivers/mfd/wl1273-core.c   
>    |  616
> +++++++++++++++++++++++++++++++++++++++
> >  include/linux/mfd/wl1273-core.h |  326
> +++++++++++++++++++++
> >  4 files changed, 950 insertions(+), 0
> deletions(-)
> >  create mode 100644 drivers/mfd/wl1273-core.c
> >  create mode 100644
> include/linux/mfd/wl1273-core.h
> > 
> > diff --git a/drivers/mfd/Kconfig
> b/drivers/mfd/Kconfig
> > index 413576a..5998a94 100644
> > --- a/drivers/mfd/Kconfig
> > +++ b/drivers/mfd/Kconfig
> > @@ -135,6 +135,12 @@ config TWL4030_CODEC
> >      select MFD_CORE
> >      default n
>
> > +config WL1273_CORE
> > +    bool
> > +    depends on I2C
> > +    select MFD_CORE
> > +    default n
> > +
> >  config MFD_TMIO
> >      bool
> >      default n
> > diff --git a/drivers/mfd/Makefile
> b/drivers/mfd/Makefile
> > index 78295d6..46e611d 100644
> > --- a/drivers/mfd/Makefile
> > +++ b/drivers/mfd/Makefile
> > @@ -30,6 +30,8 @@
> obj-$(CONFIG_TWL4030_CORE)    += twl-core.o
> twl4030-irq.o twl6030-irq.o
> >  obj-$(CONFIG_TWL4030_POWER)    +=
> twl4030-power.o
> >  obj-$(CONFIG_TWL4030_CODEC)    +=
> twl4030-codec.o
>
> > +obj-$(CONFIG_WL1273_CORE)    +=
> wl1273-core.o
> > +
> >  obj-$(CONFIG_MFD_MC13783)    +=
> mc13783-core.o
>
> >  obj-$(CONFIG_MFD_CORE)   
>     += mfd-core.o
> > diff --git a/drivers/mfd/wl1273-core.c
> b/drivers/mfd/wl1273-core.c
> > new file mode 100644
> > index 0000000..6c7dbba
> > --- /dev/null
> > +++ b/drivers/mfd/wl1273-core.c
> > @@ -0,0 +1,616 @@
> > +/*
> > + * MFD driver for wl1273 FM radio and audio codec
> submodules.
> > + *
> > + * Author:    Matti Aaltonen <matti.j.aaltonen@xxxxxxxxx>
> > + *
> > + * Copyright:   (C) 2010 Nokia
> Corporation
> > + *
> > + * This program is free software; you can
> redistribute it and/or modify
> > + * it under the terms of the GNU General Public
> License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + * 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., 51 Franklin St, Fifth Floor,
> Boston, MA
> > + * 02110-1301 USA
> > + *
> > + */
> > +
> > +#undef DEBUG
> > +
> > +#include <asm/unaligned.h>
> > +#include <linux/completion.h>
> > +#include <linux/delay.h>
> > +#include <linux/i2c.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/module.h>
> > +#include <linux/types.h>
> > +#include <linux/kernel.h>
> > +#include <linux/fs.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/mfd/core.h>
> > +#include <linux/mfd/wl1273-core.h>
> > +#include <media/v4l2-common.h>
> > +
> > +#define DRIVER_DESC "WL1273 FM Radio Core"
> > +
> > +#define
> WL1273_IRQ_MASK     (WL1273_FR_EVENT   
>     |    \
> > +       
>       WL1273_POW_ENB_EVENT)
> > +
> > +static const struct band_info bands[] = {
> > +    /* USA & Europe */
> > +    {
> > +       
> .bottom_frequency    = 87500,
> > +       
> .top_frequency        =
> 108000,
> > +       
> .band       
>     = V4L2_FM_BAND_OTHER,
> > +    },
> > +    /* Japan */
> > +    {
> > +       
> .bottom_frequency    = 76000,
> > +       
> .top_frequency        =
> 90000,
> > +       
> .band       
>     = V4L2_FM_BAND_JAPAN,
> > +    },
> > +};
> > +
> > +/*
> > + * static unsigned char radio_band - Band
> > + *
> > + * The bands are 0=Japan, 1=USA-Europe. USA-Europe is
> the default.
> > + */
> > +static unsigned char radio_band = 1;
> > +module_param(radio_band, byte, 0);
> > +MODULE_PARM_DESC(radio_band, "Band: 0=Japan,
> 1=USA-Europe*");
> > +
> > +/*
> > + * static unsigned int rds_buf - the number of RDS
> buffer blocks used.
> > + *
> > + * The default number is 100.
> > + */
> > +static unsigned int rds_buf = 100;
> > +module_param(rds_buf, uint, 0);
> > +MODULE_PARM_DESC(rds_buf, "RDS buffer entries:
> *100*");
> > +
> > +int wl1273_fm_read_reg(struct wl1273_core *core, u8
> reg, u16 *value)
> > +{
> > +    struct i2c_client *client =
> core->i2c_dev;
> > +    u8 b[2];
> > +    int r;
> > +
> > +    r =
> i2c_smbus_read_i2c_block_data(client, reg, 2, b);
> > +    if (r != 2) {
> > +       
> dev_err(&client->dev, "%s: Read: %d fails.\n",
> __func__, reg);
> > +        return
> -EREMOTEIO;
> > +    }
> > +
> > +    *value = (u16)b[0] << 8 |
> b[1];
> > +
> > +    return 0;
> > +}
> > +EXPORT_SYMBOL(wl1273_fm_read_reg);
> > +
> > +int wl1273_fm_write_cmd(struct wl1273_core *core, u8
> cmd, u16 param)
> > +{
> > +    struct i2c_client *client =
> core->i2c_dev;
> > +    u8 buf[] = { (param >> 8)
> & 0xff, param & 0xff };
> > +    int r;
> > +
> > +    r =
> i2c_smbus_write_i2c_block_data(client, cmd, 2, buf);
> > +    if (r) {
> > +       
> dev_err(&client->dev, "%s: Cmd: %d fails.\n",
> __func__, cmd);
> > +        return r;
> > +    }
> > +
> > +    return 0;
> > +}
> > +EXPORT_SYMBOL(wl1273_fm_write_cmd);
> > +
> > +int wl1273_fm_write_data(struct wl1273_core *core, u8
> *data, u16 len)
> > +{
> > +    struct i2c_client *client =
> core->i2c_dev;
> > +    struct i2c_msg msg[1];
> > +    int r;
> > +
> > +    msg[0].addr = client->addr;
> > +    msg[0].flags = 0;
> > +    msg[0].buf = data;
> > +    msg[0].len = len;
> > +
> > +    r =
> i2c_transfer(client->adapter, msg, 1);
> > +
> > +    if (r != 1) {
> > +       
> dev_err(&client->dev, "%s: write error.\n",
> __func__);
> > +        return
> -EREMOTEIO;
> > +    }
> > +
> > +    return 0;
> > +}
> > +EXPORT_SYMBOL(wl1273_fm_write_data);
> > +
> > +/**
> > + * wl1273_fm_set_audio() -    Set
> audio mode.
> > + * @core:       
>     A pointer to the device struct.
> > + * @new_mode:       
>     The new audio mode.
> > + *
> > + * Audio modes are WL1273_AUDIO_DIGITAL and
> WL1273_AUDIO_ANALOG.
> > + */
> > +int wl1273_fm_set_audio(struct wl1273_core *core,
> unsigned int new_mode)
> > +{
> > +    int r = 0;
> > +
> > +    if (core->mode ==
> WL1273_MODE_OFF ||
> > +        core->mode ==
> WL1273_MODE_SUSPENDED)
> > +        return -EPERM;
> > +
> > +    if (core->mode ==
> WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL)
> {
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET,
> > +       
>            
> WL1273_PCM_DEF_MODE);
> > +        if (r)
> > +       
>     goto out;
> > +
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
> > +       
>            
> core->i2s_mode);
> > +        if (r)
> > +       
>     goto out;
> > +
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
> > +       
>            
> WL1273_AUDIO_ENABLE_I2S);
> > +        if (r)
> > +       
>     goto out;
> > +
> > +    } else if (core->mode ==
> WL1273_MODE_RX &&
> > +       
>    new_mode == WL1273_AUDIO_ANALOG) {
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE,
> > +       
>            
> WL1273_AUDIO_ENABLE_ANALOG);
> > +        if (r)
> > +       
>     goto out;
> > +
> > +    } else if (core->mode ==
> WL1273_MODE_TX &&
> > +       
>    new_mode == WL1273_AUDIO_DIGITAL) {
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET,
> > +       
>            
> core->i2s_mode);
> > +        if (r)
> > +       
>     goto out;
> > +
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
> > +       
>            
> WL1273_AUDIO_IO_SET_I2S);
> > +        if (r)
> > +       
>     goto out;
> > +
> > +    } else if (core->mode ==
> WL1273_MODE_TX &&
> > +       
>    new_mode == WL1273_AUDIO_ANALOG) {
> > +        r =
> wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET,
> > +       
>            
> WL1273_AUDIO_IO_SET_ANALOG);
> > +        if (r)
> > +       
>     goto out;
> > +    }
> > +
> > +    core->audio_mode = new_mode;
> > +
> > +out:
> > +    return r;
> > +}
> > +EXPORT_SYMBOL(wl1273_fm_set_audio);
> > +
> > +/**
> > + * wl1273_fm_set_volume() -    Set
> volume.
> > + * @core:       
>     A pointer to the device struct.
> > + * @volume:       
>     The new volume value.
> > + */
> > +int wl1273_fm_set_volume(struct wl1273_core *core,
> unsigned int volume)
> > +{
> > +    u16 val;
> > +    int r;
> > +
> > +    if (volume >
> WL1273_MAX_VOLUME)
> > +        return
> -EINVAL;
> > +
> > +    if (core->volume == volume)
> > +        return 0;
> > +
> > +    val = volume;
> > +    r = wl1273_fm_read_reg(core,
> WL1273_VOLUME_SET, &val);
> > +    if (r)
> > +        return r;
> > +
> > +    core->volume = volume;
> > +    return 0;
> > +}
> > +EXPORT_SYMBOL(wl1273_fm_set_volume);
> > +
> > +#define
> WL1273_RDS_FIFO_EMPTY(status)    (!(1
> << 6 &  status))
> > +
> > +#define
> WL1273_RDS_CORRECTABLE_ERROR    (1 <<
> 3)
> > +#define
> WL1273_RDS_UNCORRECTABLE_ERROR    (1 <<
> 4)
> > +
> > +static int wl1273_fm_rds(struct wl1273_core *core)
> > +{
> > +    struct i2c_client *client =
> core->i2c_dev;
> > +    struct device *dev =
> &client->dev;
> > +    struct v4l2_rds_data rds = { 0, 0,
> 0 };
> > +    struct i2c_msg msg[] = {
> > +        {
> > +       
>     .addr = client->addr,
> > +       
>     .flags = 0,
> > +       
>     .buf = b0,
> > +       
>     .len = 1
> > +        },
> > +        {
> > +       
>     .addr = client->addr,
> > +       
>     .flags = I2C_M_RD,
> > +       
>     .buf = (u8 *) &rds,
> > +       
>     .len = 3
> > +        }
> > +    };
> > +    u8 b0[] = { WL1273_RDS_DATA_GET },
> status;
> > +    u16 val;
> > +    int r;
> > +
> > +    r = wl1273_fm_read_reg(core,
> WL1273_RDS_SYNC_GET, &val);
> > +    if (r)
> > +        return r;
> > +
> > +    /* Is RDS decoder synchronized?
> */
> > +    if ((val & 0x01) == 0)
> > +        return
> -EAGAIN;
> > +
> > +    /* copy all four RDS blocks to
> internal buffer */
> > +    do {
> > +        r =
> i2c_transfer(client->adapter, msg, 2);
> > +        if (r != 2) {
> > +       
>     dev_err(dev, WL1273_FM_DRIVER_NAME
> > +       
>         ": %s: read_rds error
> r == %i)\n",
> > +       
>         __func__, r);
> > +        }
> > +
> > +        status =
> rds.block;
> > +
> > +        if
> (WL1273_RDS_FIFO_EMPTY(status))
> > +       
>     break;
> > +
> > +        /* copy RDS
> block to internal buffer */
> > +       
> memcpy(&core->buffer[core->wr_index], &rds,
> 3);
> > +       
> core->wr_index += 3;
> > +
> > +        /* copy the
> error bits to standard positions */
> > +        if
> (WL1273_RDS_UNCORRECTABLE_ERROR & status) {
> > +       
>     rds.block |= V4L2_RDS_BLOCK_ERROR;
> > +       
>     rds.block &=
> ~V4L2_RDS_BLOCK_CORRECTED;
> > +        } else if 
> (WL1273_RDS_CORRECTABLE_ERROR & status) {
> > +       
>     rds.block &= ~V4L2_RDS_BLOCK_ERROR;
> > +       
>     rds.block |= V4L2_RDS_BLOCK_CORRECTED;
> > +        } else {
> > +       
>     rds.block &= ~V4L2_RDS_BLOCK_ERROR;
> > +       
>     rds.block &=
> ~V4L2_RDS_BLOCK_CORRECTED;
> > +        }
> > +
> > +        /* wrap write
> pointer */
> > +        if
> (core->wr_index >= core->buf_size)
> > +       
>     core->wr_index = 0;
> > +
> > +        /* check for
> overflow & start over */
> > +        if
> (core->wr_index == core->rd_index) {
> > +       
>     dev_dbg(dev, "RDS OVERFLOW");
> > +
> > +       
>     core->rd_index = 0;
> > +       
>     core->wr_index = 0;
> > +       
>     break;
> > +        }
> > +    } while
> (!WL1273_RDS_FIFO_EMPTY(status));
> > +
> > +    /* wake up read queue */
> > +    if (core->wr_index !=
> core->rd_index)
> > +       
> wake_up_interruptible(&core->read_queue);
> > +
> > +    return 0;
> > +}
> > +
> > +static void wl1273_fm_rds_work(struct wl1273_core
> *core)
> > +{
> > +    wl1273_fm_rds(core);
> > +}
> > +
> > +static irqreturn_t wl1273_fm_irq_thread_handler(int
> irq, void *dev_id)
> > +{
> > +    int r;
> > +    u16 flags;
> > +    struct wl1273_core *core =
> dev_id;
> > +
> > +    r = wl1273_fm_read_reg(core,
> WL1273_FLAG_GET, &flags);
> > +    if (r)
> > +        goto out;
> > +
> > +    if (flags & WL1273_BL_EVENT)
> {
> > +       
> core->irq_received = flags;
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: BL\n");
> > +    }
> > +
> > +    if (flags & WL1273_RDS_EVENT)
> {
> > +        msleep(200);
> > +
> > +       
> wl1273_fm_rds_work(core);
> > +    }
> > +
> > +    if (flags &
> WL1273_BBLK_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: BBLK\n");
> > +
> > +    if (flags &
> WL1273_LSYNC_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: LSYNC\n");
> > +
> > +    if (flags & WL1273_LEV_EVENT)
> {
> > +        u16 level;
> > +
> > +        r =
> wl1273_fm_read_reg(core, WL1273_RSSI_LVL_GET, &level);
> > +
> > +        if (r)
> > +       
>     goto out;
> > +
> > +        if (level >
> 14)
> > +       
>     dev_dbg(&core->i2c_dev->dev,
> "IRQ: LEV: 0x%x04\n",
> > +       
>         level);
> > +    }
> > +
> > +    if (flags &
> WL1273_IFFR_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: IFFR\n");
> > +
> > +    if (flags & WL1273_PI_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: PI\n");
> > +
> > +    if (flags & WL1273_PD_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: PD\n");
> > +
> > +    if (flags &
> WL1273_STIC_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: STIC\n");
> > +
> > +    if (flags & WL1273_MAL_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: MAL\n");
> > +
> > +    if (flags &
> WL1273_POW_ENB_EVENT) {
> > +       
> complete(&core->busy);
> > +       
> dev_dbg(&core->i2c_dev->dev, "NOT BUSY\n");
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: POW_ENB\n");
> > +    }
> > +
> > +    if (flags &
> WL1273_SCAN_OVER_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: SCAN_OVER\n");
> > +
> > +    if (flags &
> WL1273_ERROR_EVENT)
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: ERROR\n");
> > +
> > +    if (flags & WL1273_FR_EVENT)
> {
> > +        u16 freq;
> > +
> > +       
> dev_dbg(&core->i2c_dev->dev, "IRQ: FR:\n");
> > +
> > +        if
> (core->mode == WL1273_MODE_RX) {
> > +       
>     r = wl1273_fm_write_cmd(core,
> WL1273_TUNER_MODE_SET,
> > +       
>            
>     TUNER_MODE_STOP_SEARCH);
> > +       
>     if (r) {
> > +       
>        
> dev_err(&core->i2c_dev->dev,
> > +       
>            
> "%s: TUNER_MODE_SET fails: %d\n",
> > +       
>            
> __func__, r);
> > +       
>         goto out;
> > +       
>     }
> > +
> > +       
>     r = wl1273_fm_read_reg(core,
> WL1273_FREQ_SET, &freq);
> > +       
>     if (r)
> > +       
>         goto out;
> > +
> > +       
>     core->rx_frequency =
> > +       
>        
> bands[core->band].bottom_frequency +
> > +       
>         freq * 50;
> > +
> > +       
>     /*
> > +       
>      *  The driver works
> better with this msleep,
> > +       
>      *  the documentation
> doesn't mention it.
> > +       
>      */
> > +       
>     msleep(10);
> 
> 
> msleep on an irq handler? You shouldn't be doing it! You're
> not allowed to sleep
> during IRQ time. Kernel can panic here. You'll probably
> need to defer work and
> handle it outside irq time.
> > +
> > +       
>     dev_dbg(&core->i2c_dev->dev,
> "%dkHz\n",
> > +       
>        
> core->rx_frequency);
> > +
> > +        } else {
> > +       
>     r = wl1273_fm_read_reg(core,
> WL1273_CHANL_SET, &freq);
> > +       
>     if (r)
> > +       
>         goto out;
> > +
> > +       
>     dev_dbg(&core->i2c_dev->dev,
> "%dkHz\n", freq);
> > +        }
> > +       
> dev_dbg(&core->i2c_dev->dev, "%s: NOT BUSY\n",
> __func__);
> > +    }
> > +
> > +out:
> > +    wl1273_fm_write_cmd(core,
> WL1273_INT_MASK_SET,
> > +       
>         core->irq_flags);
> > +    complete(&core->busy);
> > +
> > +    return IRQ_HANDLED;
> > +}
> > +
> > +static struct i2c_device_id wl1273_driver_id_table[]
> = {
> > +    { WL1273_FM_DRIVER_NAME, 0 },
> > +    { }
> > +};
> > +MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table);
> > +
> > +static int wl1273_core_remove(struct i2c_client
> *client)
> > +{
> > +    struct wl1273_core *core =
> i2c_get_clientdata(client);
> > +    struct wl1273_fm_platform_data
> *pdata =
> > +       
> client->dev.platform_data;
> > +
> > +    dev_dbg(&client->dev,
> "%s\n", __func__);
> > +
> > +   
> mfd_remove_devices(&client->dev);
> > +    i2c_set_clientdata(client, core);
> > +
> > +    free_irq(client->irq, core);
> > +    pdata->free_resources();
> > +
> > +    kfree(core->buffer);
> > +    kfree(core);
> > +
> > +    return 0;
> > +}
> > +
> > +static int __devinit wl1273_core_probe(struct
> i2c_client *client,
> > +       
>            
>    const struct i2c_device_id *id)
> > +{
> > +    struct wl1273_fm_platform_data
> *pdata = client->dev.platform_data;
> > +    int r = 0;
> > +    struct wl1273_core *core;
> > +    int children = 0;
> > +
> > +    dev_dbg(&client->dev,
> "%s\n", __func__);
> > +
> > +    if (!pdata) {
> > +       
> dev_err(&client->dev, "No platform data.\n");
> > +        return
> -EINVAL;
> > +    }
> > +
> > +    core = kzalloc(sizeof(*core),
> GFP_KERNEL);
> > +    if (!core)
> > +        return
> -ENOMEM;
> > +
> > +    /* RDS buffer allocation */
> > +    core->buf_size = rds_buf * 3;
> > +    core->buffer =
> kmalloc(core->buf_size, GFP_KERNEL);
> > +    if (!core->buffer) {
> > +       
> dev_err(&client->dev,
> > +       
>     "Cannot allocate memory for RDS
> buffer.\n");
> > +        r = -ENOMEM;
> > +        goto
> err_kmalloc;
> > +    }
> > +
> > +    core->irq_flags =
> WL1273_IRQ_MASK;
> > +    core->i2c_dev = client;
> > +    core->rds_on = false;
> > +    core->mode = WL1273_MODE_OFF;
> > +    core->tx_power = 4;
> > +    core->audio_mode =
> WL1273_AUDIO_ANALOG;
> > +    core->band = radio_band;
> > +    core->bands = bands;
> > +    core->number_of_bands =
> ARRAY_SIZE(bands);
> > +    core->i2s_mode =
> WL1273_I2S_DEF_MODE;
> > +    core->channel_number = 2;
> > +    core->volume =
> WL1273_DEFAULT_VOLUME;
> > +    core->rx_frequency =
> bands[core->band].bottom_frequency;
> > +    core->tx_frequency =
> bands[core->band].top_frequency;
> > +
> > +    dev_dbg(&client->dev,
> "radio_band: %d\n", radio_band);
> > +
> > +    mutex_init(&core->lock);
> > +
> > +    pdata =
> client->dev.platform_data;
> > +    if (pdata) {
> > +        r =
> pdata->request_resources(client);
> > +        if (r) {
> > +       
>     dev_err(&client->dev,
> WL1273_FM_DRIVER_NAME
> > +       
>         ": Cannot get platform
> data\n");
> > +       
>     goto err_new_mixer;
> > +        }
> > +
> > +        r =
> request_threaded_irq(client->irq, NULL,
> > +       
>        
>      wl1273_fm_irq_thread_handler,
> > +       
>        
>      IRQF_ONESHOT |
> IRQF_TRIGGER_FALLING,
> > +       
>        
>      "wl1273-fm", core);
> > +        if (r < 0)
> {
> > +       
>     dev_err(&client->dev,
> WL1273_FM_DRIVER_NAME
> > +       
>         ": Unable to register
> IRQ handler\n");
> > +       
>     goto err_request_irq;
> > +        }
> > +    } else {
> > +       
> dev_err(&client->dev, WL1273_FM_DRIVER_NAME ": Core
> WL1273 IRQ"
> > +       
>     " not configured");
> > +        r = -EINVAL;
> > +        goto
> err_new_mixer;
> > +    }
> > +
> > +   
> init_completion(&core->busy);
> > +   
> init_waitqueue_head(&core->read_queue);
> > +
> > +    i2c_set_clientdata(client, core);
> > +
> > +    if (pdata->children &
> WL1273_RADIO_CHILD) {
> > +        struct mfd_cell
> *cell = &core->cells[children];
> > +       
> dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__);
> > +        cell->name =
> "wl1273_fm_radio";
> > +       
> cell->platform_data = &core;
> > +       
> cell->data_size = sizeof(core);
> > +        children++;
> > +    }
> > +
> > +    if (pdata->children &
> WL1273_CODEC_CHILD) {
> > +        struct mfd_cell
> *cell = &core->cells[children];
> > +       
> dev_dbg(&client->dev, "%s: Have codec.\n",
> __func__);
> > +        cell->name =
> "wl1273_codec_audio";
> > +       
> cell->platform_data = &core;
> > +       
> cell->data_size = sizeof(core);
> > +        children++;
> > +    }
> > +
> > +    if (children) {
> > +       
> dev_dbg(&client->dev, "%s: Have children.\n",
> __func__);
> > +        r =
> mfd_add_devices(&client->dev, -1, core->cells,
> > +       
>            
> children, NULL, 0);
> > +    } else {
> > +       
> dev_err(&client->dev, "No platform data found for
> children.\n");
> > +        r = -ENODEV;
> > +    }
> > +
> > +    if (!r)
> > +        return 0;
> > +
> > +    i2c_set_clientdata(client, NULL);
> > +    kfree(core);
> > +    free_irq(client->irq, core);
> > +err_request_irq:
> > +    pdata->free_resources();
> > +err_new_mixer:
> > +    kfree(core->buffer);
> > +err_kmalloc:
> > +    kfree(core);
> > +    dev_dbg(&client->dev,
> "%s\n", __func__);
> > +
> > +    return r;
> > +}
> > +
> > +static struct i2c_driver wl1273_core_driver = {
> > +    .driver = {
> > +        .name =
> WL1273_FM_DRIVER_NAME,
> > +    },
> > +    .probe = wl1273_core_probe,
> > +    .id_table =
> wl1273_driver_id_table,
> > +    .remove =
> __devexit_p(wl1273_core_remove),
> > +};
> > +
> > +static int __init wl1273_core_init(void)
> > +{
> > +    int r;
> > +
> > +    r =
> i2c_add_driver(&wl1273_core_driver);
> > +    if (r) {
> > +       
> pr_err(WL1273_FM_DRIVER_NAME
> > +           
>    ": driver registration failed\n");
> > +        return r;
> > +    }
> > +
> > +    return 0;
> > +}
> > +
> > +static void __exit wl1273_core_exit(void)
> > +{
> > +    flush_scheduled_work();
> > +
> > +   
> i2c_del_driver(&wl1273_core_driver);
> > +}
> > +late_initcall(wl1273_core_init);
> > +module_exit(wl1273_core_exit);
> > +
> > +MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@xxxxxxxxx>");
> > +MODULE_DESCRIPTION(DRIVER_DESC);
> > +MODULE_LICENSE("GPL");
> > diff --git a/include/linux/mfd/wl1273-core.h
> b/include/linux/mfd/wl1273-core.h
> > new file mode 100644
> > index 0000000..81c9743
> > --- /dev/null
> > +++ b/include/linux/mfd/wl1273-core.h
> > @@ -0,0 +1,326 @@
> > +/*
> > + * include/media/radio/radio-wl1273.h
> > + *
> > + * Some definitions for the wl1273 radio
> receiver/transmitter chip.
> > + *
> > + * Copyright (C) Nokia Corporation
> > + * Author: Matti J. Aaltonen <matti.j.aaltonen@xxxxxxxxx>
> > + *
> > + * This program is free software; you can
> redistribute it and/or
> > + * modify it under the terms of the GNU General
> Public License
> > + * version 2 as published by the Free Software
> Foundation.
> > + *
> > + * 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., 51 Franklin St, Fifth Floor,
> Boston, MA
> > + * 02110-1301 USA
> > + */
> > +
> > +#ifndef RADIO_WL1273_H
> > +#define RADIO_WL1273_H
> > +
> > +#include <linux/i2c.h>
> > +#include <linux/mfd/core.h>
> > +
> > +#define WL1273_FM_DRIVER_NAME   
> "wl1273-fm"
> > +#define RX71_FM_I2C_ADDR    0x22
> > +
> > +#define WL1273_STEREO_GET   
>     0
> > +#define WL1273_RSSI_LVL_GET   
>     1
> > +#define WL1273_IF_COUNT_GET   
>     2
> > +#define WL1273_FLAG_GET   
>         3
> > +#define WL1273_RDS_SYNC_GET   
>     4
> > +#define WL1273_RDS_DATA_GET   
>     5
> > +#define WL1273_FREQ_SET   
>         10
> > +#define WL1273_AF_FREQ_SET   
>     11
> > +#define WL1273_MOST_MODE_SET   
>     12
> > +#define WL1273_MOST_BLEND_SET   
>     13
> > +#define WL1273_DEMPH_MODE_SET   
>     14
> > +#define WL1273_SEARCH_LVL_SET   
>     15
> > +#define WL1273_BAND_SET   
>         16
> > +#define WL1273_MUTE_STATUS_SET   
>     17
> > +#define WL1273_RDS_PAUSE_LVL_SET   
> 18
> > +#define WL1273_RDS_PAUSE_DUR_SET   
> 19
> > +#define WL1273_RDS_MEM_SET   
>     20
> > +#define WL1273_RDS_BLK_B_SET   
>     21
> > +#define WL1273_RDS_MSK_B_SET   
>     22
> > +#define WL1273_RDS_PI_MASK_SET   
>     23
> > +#define WL1273_RDS_PI_SET   
>     24
> > +#define WL1273_RDS_SYSTEM_SET   
>     25
> > +#define WL1273_INT_MASK_SET   
>     26
> > +#define WL1273_SEARCH_DIR_SET   
>     27
> > +#define WL1273_VOLUME_SET   
>     28
> > +#define WL1273_AUDIO_ENABLE   
>     29
> > +#define WL1273_PCM_MODE_SET   
>     30
> > +#define WL1273_I2S_MODE_CONFIG_SET   
> 31
> > +#define WL1273_POWER_SET   
>     32
> > +#define WL1273_INTX_CONFIG_SET   
>     33
> > +#define WL1273_PULL_EN_SET   
>     34
> > +#define WL1273_HILO_SET   
>         35
> > +#define WL1273_SWITCH2FREF   
>     36
> > +#define WL1273_FREQ_DRIFT_REPORT   
> 37
> > +
> > +#define WL1273_PCE_GET   
>         40
> > +#define WL1273_FIRM_VER_GET   
>     41
> > +#define WL1273_ASIC_VER_GET   
>     42
> > +#define WL1273_ASIC_ID_GET   
>     43
> > +#define WL1273_MAN_ID_GET   
>     44
> > +#define WL1273_TUNER_MODE_SET   
>     45
> > +#define WL1273_STOP_SEARCH   
>     46
> > +#define WL1273_RDS_CNTRL_SET   
>     47
> > +
> > +#define WL1273_WRITE_HARDWARE_REG   
> 100
> > +#define WL1273_CODE_DOWNLOAD   
>     101
> > +#define WL1273_RESET   
>         102
> > +
> > +#define WL1273_FM_POWER_MODE   
>     254
> > +#define WL1273_FM_INTERRUPT   
>     255
> > +
> > +/* Transmitter API */
> > +
> > +#define WL1273_CHANL_SET   
>     55
> > +#define WL1273_SCAN_SPACING_SET   
>     56
> > +#define WL1273_REF_SET   
>         57
> > +#define WL1273_POWER_ENB_SET   
>     90
> > +#define WL1273_POWER_ATT_SET   
>     58
> > +#define WL1273_POWER_LEV_SET   
>     59
> > +#define WL1273_AUDIO_DEV_SET   
>     60
> > +#define WL1273_PILOT_DEV_SET   
>     61
> > +#define WL1273_RDS_DEV_SET   
>     62
> > +#define WL1273_PUPD_SET   
>         91
> > +#define WL1273_AUDIO_IO_SET   
>     63
> > +#define WL1273_PREMPH_SET   
>     64
> > +#define WL1273_MONO_SET   
>         66
> > +#define WL1273_MUTE   
>         92
> > +#define WL1273_MPX_LMT_ENABLE   
>     67
> > +#define WL1273_PI_SET   
>         93
> > +#define WL1273_ECC_SET   
>         69
> > +#define WL1273_PTY   
>         70
> > +#define WL1273_AF   
>         71
> > +#define WL1273_DISPLAY_MODE   
>     74
> > +#define WL1273_RDS_REP_SET   
>     77
> > +#define WL1273_RDS_CONFIG_DATA_SET   
> 98
> > +#define WL1273_RDS_DATA_SET   
>     99
> > +#define WL1273_RDS_DATA_ENB   
>     94
> > +#define WL1273_TA_SET   
>         78
> > +#define WL1273_TP_SET   
>         79
> > +#define WL1273_DI_SET   
>         80
> > +#define WL1273_MS_SET   
>         81
> > +#define WL1273_PS_SCROLL_SPEED   
>     82
> > +#define WL1273_TX_AUDIO_LEVEL_TEST   
> 96
> > +#define
> WL1273_TX_AUDIO_LEVEL_TEST_THRESHOLD    73
> > +#define
> WL1273_TX_AUDIO_INPUT_LEVEL_RANGE_SET    54
> > +#define WL1273_RX_ANTENNA_SELECT   
> 87
> > +#define WL1273_I2C_DEV_ADDR_SET   
>     86
> > +#define
> WL1273_REF_ERR_CALIB_PARAM_SET   
>     88
> > +#define
> WL1273_REF_ERR_CALIB_PERIODICITY_SET    89
> > +#define WL1273_SOC_INT_TRIGGER   
>         52
> > +#define WL1273_SOC_AUDIO_PATH_SET   
>     83
> > +#define WL1273_SOC_PCMI_OVERRIDE   
>     84
> > +#define WL1273_SOC_I2S_OVERRIDE   
>     85
> > +#define
> WL1273_RSSI_BLOCK_SCAN_FREQ_SET    95
> > +#define
> WL1273_RSSI_BLOCK_SCAN_START    97
> > +#define
> WL1273_RSSI_BLOCK_SCAN_DATA_GET     5
> > +#define
> WL1273_READ_FMANT_TUNE_VALUE   
>     104
> > +
> > +#define WL1273_RDS_OFF   
>     0
> > +#define WL1273_RDS_ON   
>     1
> > +#define WL1273_RDS_RESET    2
> > +
> > +#define WL1273_AUDIO_DIGITAL    0
> > +#define WL1273_AUDIO_ANALOG    1
> > +
> > +#define WL1273_MODE_RX   
>     0
> > +#define WL1273_MODE_TX   
>     1
> > +#define WL1273_MODE_OFF   
>     2
> > +#define WL1273_MODE_SUSPENDED    3
> > +
> > +#define WL1273_RADIO_CHILD    (1
> << 0)
> > +#define WL1273_CODEC_CHILD    (1
> << 1)
> > +
> > +#define WL1273_RX_MONO   
>     1
> > +#define WL1273_RX_STEREO    0
> > +#define WL1273_TX_MONO   
>     0
> > +#define WL1273_TX_STEREO    1
> > +
> > +#define WL1273_MAX_VOLUME    0xffff
> > +#define WL1273_DEFAULT_VOLUME   
> 0x78b8
> > +
> > +/* I2S protocol, left channel first, data width 16
> bits */
> > +#define WL1273_PCM_DEF_MODE   
>     0x00
> > +
> > +/* Rx */
> > +#define WL1273_AUDIO_ENABLE_I2S   
>     (1 << 0)
> > +#define WL1273_AUDIO_ENABLE_ANALOG   
> (1 << 1)
> > +
> > +/* Tx */
> > +#define WL1273_AUDIO_IO_SET_ANALOG   
> 0
> > +#define WL1273_AUDIO_IO_SET_I2S   
>     1
> > +
> > +#define WL1273_POWER_SET_OFF   
>     0
> > +#define WL1273_POWER_SET_FM   
>     (1 << 0)
> > +#define WL1273_POWER_SET_RDS   
>     (1 << 1)
> > +#define WL1273_POWER_SET_RETENTION   
> (1 << 4)
> > +
> > +#define WL1273_PUPD_SET_OFF   
>     0x00
> > +#define WL1273_PUPD_SET_ON   
>     0x01
> > +#define WL1273_PUPD_SET_RETENTION   
> 0x10
> > +
> > +/* I2S mode */
> > +#define WL1273_IS2_WIDTH_32    0x0
> > +#define WL1273_IS2_WIDTH_40    0x1
> > +#define WL1273_IS2_WIDTH_22_23    0x2
> > +#define WL1273_IS2_WIDTH_23_22    0x3
> > +#define WL1273_IS2_WIDTH_48    0x4
> > +#define WL1273_IS2_WIDTH_50    0x5
> > +#define WL1273_IS2_WIDTH_60    0x6
> > +#define WL1273_IS2_WIDTH_64    0x7
> > +#define WL1273_IS2_WIDTH_80    0x8
> > +#define WL1273_IS2_WIDTH_96    0x9
> > +#define WL1273_IS2_WIDTH_128    0xa
> > +#define WL1273_IS2_WIDTH    0xf
> > +
> > +#define WL1273_IS2_FORMAT_STD    (0x0
> << 4)
> > +#define WL1273_IS2_FORMAT_LEFT    (0x1
> << 4)
> > +#define WL1273_IS2_FORMAT_RIGHT   
> (0x2 << 4)
> > +#define WL1273_IS2_FORMAT_USER    (0x3
> << 4)
> > +
> > +#define WL1273_IS2_MASTER    (0x0
> << 6)
> > +#define WL1273_IS2_SLAVEW    (0x1
> << 6)
> > +
> > +#define
> WL1273_IS2_TRI_AFTER_SENDING    (0x0 <<
> 7)
> > +#define
> WL1273_IS2_TRI_ALWAYS_ACTIVE    (0x1 <<
> 7)
> > +
> > +#define WL1273_IS2_SDOWS_RR    (0x0
> << 8)
> > +#define WL1273_IS2_SDOWS_RF    (0x1
> << 8)
> > +#define WL1273_IS2_SDOWS_FR    (0x2
> << 8)
> > +#define WL1273_IS2_SDOWS_FF    (0x3
> << 8)
> > +
> > +#define WL1273_IS2_TRI_OPT    (0x0
> << 10)
> > +#define WL1273_IS2_TRI_ALWAYS    (0x1
> << 10)
> > +
> > +#define WL1273_IS2_RATE_48K    (0x0
> << 12)
> > +#define WL1273_IS2_RATE_44_1K    (0x1
> << 12)
> > +#define WL1273_IS2_RATE_32K    (0x2
> << 12)
> > +#define WL1273_IS2_RATE_22_05K    (0x4
> << 12)
> > +#define WL1273_IS2_RATE_16K    (0x5
> << 12)
> > +#define WL1273_IS2_RATE_12K    (0x8
> << 12)
> > +#define WL1273_IS2_RATE_11_025    (0x9
> << 12)
> > +#define WL1273_IS2_RATE_8K    (0xa
> << 12)
> > +#define WL1273_IS2_RATE   
>     (0xf << 12)
> > +
> > +#define WL1273_I2S_DEF_MODE   
> (WL1273_IS2_WIDTH_32 | \
> > +       
>    
>      WL1273_IS2_FORMAT_STD | \
> > +       
>    
>      WL1273_IS2_MASTER | \
> > +       
>    
>      WL1273_IS2_TRI_AFTER_SENDING |
> \
> > +       
>    
>      WL1273_IS2_SDOWS_RR | \
> > +       
>    
>      WL1273_IS2_TRI_OPT | \
> > +       
>    
>      WL1273_IS2_RATE_48K)
> > +
> > +/* Private IOCTL */
> > +#define WL1273_CID_FM_BAND   
> (V4L2_CID_PRIVATE_BASE + 2)
> > +
> > +#define SCHAR_MIN (-128)
> > +#define SCHAR_MAX 127
> > +
> > +#define WL1273_FR_EVENT   
>         (1 << 0)
> > +#define WL1273_BL_EVENT   
>         (1 << 1)
> > +#define WL1273_RDS_EVENT   
>     (1 << 2)
> > +#define WL1273_BBLK_EVENT   
>     (1 << 3)
> > +#define WL1273_LSYNC_EVENT   
>     (1 << 4)
> > +#define WL1273_LEV_EVENT   
>     (1 << 5)
> > +#define WL1273_IFFR_EVENT   
>     (1 << 6)
> > +#define WL1273_PI_EVENT   
>         (1 << 7)
> > +#define WL1273_PD_EVENT   
>         (1 << 8)
> > +#define WL1273_STIC_EVENT   
>     (1 << 9)
> > +#define WL1273_MAL_EVENT   
>     (1 << 10)
> > +#define WL1273_POW_ENB_EVENT   
>     (1 << 11)
> > +#define WL1273_SCAN_OVER_EVENT   
>     (1 << 12)
> > +#define WL1273_ERROR_EVENT   
>     (1 << 13)
> > +
> > +#define TUNER_MODE_STOP_SEARCH   
>     0
> > +#define TUNER_MODE_PRESET   
>     1
> > +#define TUNER_MODE_AUTO_SEEK   
>     2
> > +#define TUNER_MODE_AF   
>         3
> > +#define TUNER_MODE_AUTO_SEEK_PI   
>     4
> > +#define TUNER_MODE_AUTO_SEEK_BULK   
> 5
> > +
> > +/* Allowed modes */
> > +#define WL1273_RX_ALLOWED    0x01
> > +#define WL1273_TX_ALLOWED    0x02
> > +#define WL1273_RXTX_ALLOWED   
> (WL1273_RX_ALLOWED | WL1273_TX_ALLOWED)
> > +
> > +struct band_info {
> > +    u32 bottom_frequency;
> > +    u32 top_frequency;
> > +    u8 band;
> > +};
> > +
> > +struct wl1273_fm_platform_data {
> > +    int (*request_resources) (struct
> i2c_client *client);
> > +    void (*free_resources) (void);
> > +    void (*enable) (void);
> > +    void (*disable) (void);
> > +
> > +    u8 modes;
> > +    unsigned int children;
> > +};
> > +
> > +#define WL1273_FM_CORE_CELLS    2
> > +
> > +/* Allowed modes */
> > +#define WL1273_RX_ALLOWED    0x01
> > +#define WL1273_TX_ALLOWED    0x02
> > +#define WL1273_RXTX_ALLOWED   
> (WL1273_RX_ALLOWED | WL1273_TX_ALLOWED)
> > +
> > +struct wl1273_core {
> > +    struct mfd_cell
> cells[WL1273_FM_CORE_CELLS];
> > +    struct i2c_client *i2c_dev;
> > +
> > +     u8 allowed_modes;
> > +    unsigned int mode;
> > +    unsigned int preemphasis;
> > +    unsigned int audio_mode;
> > +    unsigned int spacing;
> > +    unsigned int tx_power;
> > +    unsigned int rx_frequency;
> > +    unsigned int tx_frequency;
> > +    unsigned int band;
> > +    unsigned int i2s_mode;
> > +    unsigned int channel_number;
> > +    unsigned int number_of_bands;
> > +    unsigned int volume;
> > +
> > +    const struct band_info *bands;
> > +
> > +    /* RDS */
> > +    bool rds_on;
> > +    struct delayed_work work;
> > +
> > +    wait_queue_head_t read_queue;
> > +    struct mutex lock; /* for
> serializing fm radio operations */
> > +    struct completion busy;
> > +
> > +    unsigned char *buffer;
> > +    unsigned int buf_size;
> > +    unsigned int rd_index;
> > +    unsigned int wr_index;
> > +
> > +    /* Selected interrupts */
> > +    u16 irq_flags;
> > +    u16 irq_received;
> > +};
> > +
> > +int wl1273_fm_write_cmd(struct wl1273_core *core, u8
> cmd, u16 param);
> > +int wl1273_fm_write_data(struct wl1273_core *core, u8
> *data, u16 len);
> > +int wl1273_fm_read_reg(struct wl1273_core *core, u8
> reg, u16 *value);
> > +
> > +int wl1273_fm_set_audio(struct wl1273_core *core,
> unsigned int mode);
> > +int wl1273_fm_set_volume(struct wl1273_core *core,
> unsigned int volume);
> > +
> > +#endif    /* ifndef RADIO_WL1273_H */
> 
> --
> To unsubscribe from this list: send the line "unsubscribe
> linux-media" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux