Re: [PATCH v3 2/6] Add the main bulk of core driver for SI476x code
Hi Andrey, I'm really sorry for the long delay, but I finally have time to review v4 of this code. On Tue October 23 2012 20:44:28 Andrey Smirnov wrote: This patch adds main part(out of three) of the I2C driver for the core of MFD device. Signed-off-by: Andrey Smirnov andrey.smir...@convergeddevices.net --- drivers/mfd/si476x-i2c.c | 966 ++ 1 file changed, 966 insertions(+) create mode 100644 drivers/mfd/si476x-i2c.c diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c new file mode 100644 index 000..6d581bd --- /dev/null +++ b/drivers/mfd/si476x-i2c.c @@ -0,0 +1,966 @@ +/* + * include/media/si476x-i2c.c -- Core device driver for si476x MFD + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * + * Author: Andrey Smirnov andrey.smir...@convergeddevices.net + * + * 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; version 2 of the License. + * + * 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. + * + */ +#include linux/module.h + +#include linux/slab.h +#include linux/interrupt.h +#include linux/delay.h +#include linux/gpio.h +#include linux/regulator/consumer.h +#include linux/i2c.h +#include linux/err.h + +#include linux/mfd/si476x-core.h + +/* Command Timeouts */ +#define DEFAULT_TIMEOUT 10 +#define TIMEOUT_TUNE 70 +#define TIMEOUT_POWER_UP 33 + +#define MAX_IO_ERRORS 10 + +#define SI476X_DRIVER_RDS_FIFO_DEPTH 128 + +#define SI476X_STATUS_POLL_US 0 + +/** + * si476x_core_config_pinmux() - pin function configuration function + * + * @core: Core device structure + * + * Configure the functions of the pins of the radio chip. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +static int si476x_core_config_pinmux(struct si476x_core *core) +{ + int err; + dev_dbg(core-client-dev, Configuring pinmux\n); + err = si476x_core_cmd_dig_audio_pin_cfg(core, + core-pinmux.dclk, + core-pinmux.dfs, + core-pinmux.dout, + core-pinmux.xout); + if (err 0) { + dev_err(core-client-dev, + Failed to configure digital audio pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_zif_pin_cfg(core, + core-pinmux.iqclk, + core-pinmux.iqfs, + core-pinmux.iout, + core-pinmux.qout); + if (err 0) { + dev_err(core-client-dev, + Failed to configure ZIF pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core, + core-pinmux.icin, + core-pinmux.icip, + core-pinmux.icon, + core-pinmux.icop); + if (err 0) { + dev_err(core-client-dev, + Failed to configure IC-Link/GPO pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_ana_audio_pin_cfg(core, + core-pinmux.lrout); + if (err 0) { + dev_err(core-client-dev, + Failed to configure analog audio pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_intb_pin_cfg(core, +core-pinmux.intb, +core-pinmux.a1); + if (err 0) { + dev_err(core-client-dev, + Failed to configure interrupt pins(err = %d)\n, + err); + return err; + } + + return 0; +} + +static inline void si476x_core_schedule_polling_work(struct si476x_core *core) +{ + schedule_delayed_work(core-status_monitor, + usecs_to_jiffies(atomic_read(core-polling_interval))); +} + +/** + * si476x_core_start() - early chip startup function + * @core: Core device structure + * @soft: When set, this flag forces soft startup, where soft
Re: [PATCH v3 2/6] Add the main bulk of core driver for SI476x code
On Thu, Oct 25, 2012 at 03:26:02PM -0700, Andrey Smirnov wrote: On 10/25/2012 12:45 PM, Mark Brown wrote: This really makes little sense to me, why are you doing this? Does the device *really* layer a byte stream on top of I2C for sending messages that look like marshalled register reads and writes? The SI476x chips has a concept of a property. Each property having 16-bit address and 16-bit value. At least a portion of a chip configuration is done by modifying those properties. In order to Right, that's what I remembered from previous code. There's no way this should be a regmap bus - a bus is something that gets data serialised by the core into a byte stream, having the data rendered down into a byte stream and then reparsing it is a bit silly. The device should be hooking in before the data gets marshalled which we can't currently do but it shouldn't be too hard to make it so that we can have register read and write functions supplied in the regmap config. Also due to the way the driver uses the chip it is only powered up when the corresponding file in devfs(e.g. /dev/radio0) is opened at least by one user which means that unless there is a user who opened the file all the SET/GET_PROPERTY commands sent to it will be lost. The codec driver for that chip does not have any say in the power management policy(while all the audio configuration is done via properties) if the chip is not powered up the driver has to cache the configuration values it has so that they can be applied later. This is very normal, indeed modern CODEC drivers can leave the chip powered down whenever it's not performing some function. So, since I have to implement a caching functionality in the driver, in order to avoid reinventing the wheel I opted for using 'regmap' API for this. Of course, It is possible that I misunderstood the purpose and capabilities of the 'regmap' framework, which would make my code look very silly indeed. If that is the case I'll just re-implement it using some sort of ad-hoc version of caching. No, what you're doing is totally sensible. It needs a bit of extension to the framework before you can do it though. signature.asc Description: Digital signature
Re: [PATCH v3 2/6] Add the main bulk of core driver for SI476x code
On 10/27/2012 02:31 PM, Mark Brown wrote: On Thu, Oct 25, 2012 at 03:26:02PM -0700, Andrey Smirnov wrote: On 10/25/2012 12:45 PM, Mark Brown wrote: This really makes little sense to me, why are you doing this? Does the device *really* layer a byte stream on top of I2C for sending messages that look like marshalled register reads and writes? The SI476x chips has a concept of a property. Each property having 16-bit address and 16-bit value. At least a portion of a chip configuration is done by modifying those properties. In order to Right, that's what I remembered from previous code. There's no way this should be a regmap bus - a bus is something that gets data serialised by the core into a byte stream, having the data rendered down into a byte stream and then reparsing it is a bit silly. The device should be hooking in before the data gets marshalled which we can't currently do but it shouldn't be too hard to make it so that we can have register read and write functions supplied in the regmap config. Oh, now I think I see what you mean. I have two agree with you, I don't think I like what I am doing in my code. I'll try to familiarize myself with 'regmap' code and come up with the way to extend the framework. And I just wanted to upstream my simple radio driver... :-) -- To unsubscribe from this list: send the line unsubscribe linux-media in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v3 2/6] Add the main bulk of core driver for SI476x code
On Tue, Oct 23, 2012 at 11:44:28AM -0700, Andrey Smirnov wrote: + core-regmap = devm_regmap_init_si476x(core); + if (IS_ERR(core-regmap)) { This really makes little sense to me, why are you doing this? Does the device *really* layer a byte stream on top of I2C for sending messages that look like marshalled register reads and writes? signature.asc Description: Digital signature
Re: [PATCH v3 2/6] Add the main bulk of core driver for SI476x code
On 10/25/2012 12:45 PM, Mark Brown wrote: On Tue, Oct 23, 2012 at 11:44:28AM -0700, Andrey Smirnov wrote: +core-regmap = devm_regmap_init_si476x(core); +if (IS_ERR(core-regmap)) { This really makes little sense to me, why are you doing this? Does the device *really* layer a byte stream on top of I2C for sending messages that look like marshalled register reads and writes? The SI476x chips has a concept of a property. Each property having 16-bit address and 16-bit value. At least a portion of a chip configuration is done by modifying those properties. In order to manipulate those properties user is expected to issue what is called a command. For manipulating properties there are two commands: - SET_PROPERTY which is I2C write of 6 bytes of the following layout: | 0x13 | 0x00 | Address High Byte | Address Low Byte | Property Data High Byte | Property Data Low Byte | After the command is finished being executed the 1 byte read would contain the status byte followed by 1 byte read which contain status byte - GET_PROPERTY which is I2C write of 4 bytes of the following layout: | 0x13 | 0x00 | Address High Byte | Address Low Byte | After the command is finished being executed the 4 byte read would have the following layout: | Status Byte | Reserved Byte | Property Value High Byte | Property Value Low Byte | The chip does not operate continuously in the AM/FM frequency range, instead the user is expected to power-down/power-up the chip in a certain mode which in my case can be either AM or FM tuner. There are two ways of doing that one is to send a power down command to the device, the second one is to toggle reset line of the chip. Both methods will reset the values of the aforementioned properties. Because V4L2 user-space interface presents a tuner as the one continuously operating in the whole AM/FM range it is necessary for me to do those AM/FM switches transparently when handling tuning or seeking requests from the user. That means that I need to cache the values of the properties I care about in the driver and restore them when user switches to the mode(for example when AM-FM-AM transition happens) The other quirk of that chip is that some properties are only accessible in certain modes(for example it is impossible to configure RDS interrupt sources, which is FM specific, in AM mode), but some of the controls I expose to user-land change the values of the properties and since AM/FM switches happen transparently to the user in the situation when FM specific property is changed while tuner is in AM mode, the driver has to cache the value and write it when the switch to FM would take place. Also due to the way the driver uses the chip it is only powered up when the corresponding file in devfs(e.g. /dev/radio0) is opened at least by one user which means that unless there is a user who opened the file all the SET/GET_PROPERTY commands sent to it will be lost. The codec driver for that chip does not have any say in the power management policy(while all the audio configuration is done via properties) if the chip is not powered up the driver has to cache the configuration values it has so that they can be applied later. So, since I have to implement a caching functionality in the driver, in order to avoid reinventing the wheel I opted for using 'regmap' API for this. Of course, It is possible that I misunderstood the purpose and capabilities of the 'regmap' framework, which would make my code look very silly indeed. If that is the case I'll just re-implement it using some sort of ad-hoc version of caching. -- To unsubscribe from this list: send the line unsubscribe linux-media in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
[PATCH v3 2/6] Add the main bulk of core driver for SI476x code
This patch adds main part(out of three) of the I2C driver for the core of MFD device. Signed-off-by: Andrey Smirnov andrey.smir...@convergeddevices.net --- drivers/mfd/si476x-i2c.c | 966 ++ 1 file changed, 966 insertions(+) create mode 100644 drivers/mfd/si476x-i2c.c diff --git a/drivers/mfd/si476x-i2c.c b/drivers/mfd/si476x-i2c.c new file mode 100644 index 000..6d581bd --- /dev/null +++ b/drivers/mfd/si476x-i2c.c @@ -0,0 +1,966 @@ +/* + * include/media/si476x-i2c.c -- Core device driver for si476x MFD + * device + * + * Copyright (C) 2012 Innovative Converged Devices(ICD) + * + * Author: Andrey Smirnov andrey.smir...@convergeddevices.net + * + * 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; version 2 of the License. + * + * 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. + * + */ +#include linux/module.h + +#include linux/slab.h +#include linux/interrupt.h +#include linux/delay.h +#include linux/gpio.h +#include linux/regulator/consumer.h +#include linux/i2c.h +#include linux/err.h + +#include linux/mfd/si476x-core.h + +/* Command Timeouts */ +#define DEFAULT_TIMEOUT10 +#define TIMEOUT_TUNE 70 +#define TIMEOUT_POWER_UP 33 + +#define MAX_IO_ERRORS 10 + +#define SI476X_DRIVER_RDS_FIFO_DEPTH 128 + +#define SI476X_STATUS_POLL_US 0 + +/** + * si476x_core_config_pinmux() - pin function configuration function + * + * @core: Core device structure + * + * Configure the functions of the pins of the radio chip. + * + * The function returns zero in case of succes or negative error code + * otherwise. + */ +static int si476x_core_config_pinmux(struct si476x_core *core) +{ + int err; + dev_dbg(core-client-dev, Configuring pinmux\n); + err = si476x_core_cmd_dig_audio_pin_cfg(core, + core-pinmux.dclk, + core-pinmux.dfs, + core-pinmux.dout, + core-pinmux.xout); + if (err 0) { + dev_err(core-client-dev, + Failed to configure digital audio pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_zif_pin_cfg(core, + core-pinmux.iqclk, + core-pinmux.iqfs, + core-pinmux.iout, + core-pinmux.qout); + if (err 0) { + dev_err(core-client-dev, + Failed to configure ZIF pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_ic_link_gpo_ctl_pin_cfg(core, + core-pinmux.icin, + core-pinmux.icip, + core-pinmux.icon, + core-pinmux.icop); + if (err 0) { + dev_err(core-client-dev, + Failed to configure IC-Link/GPO pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_ana_audio_pin_cfg(core, + core-pinmux.lrout); + if (err 0) { + dev_err(core-client-dev, + Failed to configure analog audio pins(err = %d)\n, + err); + return err; + } + + err = si476x_core_cmd_intb_pin_cfg(core, + core-pinmux.intb, + core-pinmux.a1); + if (err 0) { + dev_err(core-client-dev, + Failed to configure interrupt pins(err = %d)\n, + err); + return err; + } + + return 0; +} + +static inline void si476x_core_schedule_polling_work(struct si476x_core *core) +{ + schedule_delayed_work(core-status_monitor, + usecs_to_jiffies(atomic_read(core-polling_interval))); +} + +/** + * si476x_core_start() - early chip startup function + * @core: Core device structure + * @soft: When set, this flag forces soft startup, where soft + * power down is the one done by sending appropriate command instead + * of using reset pin of the tuner + * + * Perform required startup sequence to correctly power + * up the