Message ID | 20200409195841.18901-2-pierre-louis.bossart@linux.intel.com (mailing list archive) |
---|---|
State | Not Applicable, archived |
Headers | show |
Series | ASoC/SOF/clk/gpio/dt: add Hifiberry DAC+ PRO support | expand |
On Thu, Apr 09, 2020 at 02:58:26PM -0500, Pierre-Louis Bossart wrote: > The GPIOs are used e.g. on HifiBerry DAC+ HATs to control the LED > (GPIO3) and the choice of the 44.1 (GPIO6) or 48 (GPIO3) kHz > oscillator (when present). > > Enable basic gpio_chip to get/set values and get/set > directions. Tested with GPIO_LIB from sys/class/gpio, the LED turns > on/off as desired. One question, can this use existing GPIO infrastructure, like bgpio_init()? Ah, I see, that one operates over MMIO, while we would need something based on regmap API. Bartosz, do we have plans to have bgpio_regmap_init() or alike? ... > +static int pcm512x_gpio_get_direction(struct gpio_chip *chip, > + unsigned int offset) > +{ > + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); > + unsigned int val; > + int ret; > + > + ret = regmap_read(pcm512x->regmap, PCM512x_GPIO_EN, &val); > + if (ret < 0) > + return ret; > + val = (val >> offset) & 1; > + > + /* val is 0 for input, 1 for output, return inverted */ > + return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; This better to read as simple conditional, like if (val & BIT(offset)) return ..._OUT; return ..._IN; > +} ... > +static int pcm512x_gpio_direction_output(struct gpio_chip *chip, > + unsigned int offset, > + int value) > +{ > + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); > + unsigned int reg; > + int ret; > + > + /* select Register GPIOx output for OUTPUT_x (1..6) */ > + reg = PCM512x_GPIO_OUTPUT_1 + offset; > + ret = regmap_update_bits(pcm512x->regmap, reg, 0x0f, 0x02); Magic numbers detected. > + if (ret < 0) Drop unnecessary ' < 0' parts where it makes sense, like here. > + return ret; > + > + /* enable output x */ (1) > + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, > + BIT(offset), BIT(offset)); > + if (ret < 0) > + return ret; > + > + /* set value */ (2) With this (1)->(2) ordering it may be a glitch. So, first set value (if hardware allows you, otherwise it seems like a broken one), and then switch it to output. > + return regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, > + BIT(offset), value << offset); You are using many times BIT(offset) mask above, perhaps int mask = BIT(offset); Also, more robust is to use ternary here: 'value ? BIT(offset) : 0'. Rationale: think what happen with value != 1 (theoretical possibility in the future). > +} ... > +static int pcm512x_gpio_get(struct gpio_chip *chip, unsigned int offset) > +{ > + return (val >> offset) & 1; Don't forget to use BIT() macro. return !!(val & BIT(offset)); > +} ... > +static void pcm512x_gpio_set(struct gpio_chip *chip, unsigned int offset, > + int value) > +{ > + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); > + int ret; > + > + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, > + BIT(offset), value << offset); value ? BIT(offset) : 0 > + if (ret < 0) > + pr_debug("%s: regmap_update_bits failed: %d\n", __func__, ret); No __func__ in debug messages. Use dev_dbg() when we have struct device available. > +} ... > +static const struct gpio_chip template_chip = { Give better name, please. E.g. pcm512x_gpio_chip. > + .label = "pcm512x-gpio", > + .names = pcm512x_gpio_names, > + .owner = THIS_MODULE, > + .get_direction = pcm512x_gpio_get_direction, > + .direction_input = pcm512x_gpio_direction_input, > + .direction_output = pcm512x_gpio_direction_output, > + .get = pcm512x_gpio_get, > + .set = pcm512x_gpio_set, > + .base = -1, /* let gpiolib select the base */ > + .ngpio = ARRAY_SIZE(pcm512x_gpio_names), > +}; ... > + /* expose 6 GPIO pins, numbered from 1 to 6 */ > + pcm512x->chip = template_chip; > + pcm512x->chip.parent = dev; > + > + ret = devm_gpiochip_add_data(dev, &pcm512x->chip, pcm512x); > + if (ret != 0) { if (ret) > + dev_err(dev, "Failed to register gpio chip: %d\n", ret); > + goto err; > + }
>> +static int pcm512x_gpio_get_direction(struct gpio_chip *chip, >> + unsigned int offset) >> +{ >> + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); >> + unsigned int val; >> + int ret; >> + >> + ret = regmap_read(pcm512x->regmap, PCM512x_GPIO_EN, &val); >> + if (ret < 0) >> + return ret; > >> + val = (val >> offset) & 1; >> + >> + /* val is 0 for input, 1 for output, return inverted */ >> + return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; > > This better to read as simple conditional, like > > if (val & BIT(offset)) > return ..._OUT; > return ..._IN; > >> +} ok > > ... > >> +static int pcm512x_gpio_direction_output(struct gpio_chip *chip, >> + unsigned int offset, >> + int value) >> +{ >> + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); >> + unsigned int reg; >> + int ret; >> + >> + /* select Register GPIOx output for OUTPUT_x (1..6) */ >> + reg = PCM512x_GPIO_OUTPUT_1 + offset; > >> + ret = regmap_update_bits(pcm512x->regmap, reg, 0x0f, 0x02); > > Magic numbers detected. > >> + if (ret < 0) > > Drop unnecessary ' < 0' parts where it makes sense, like here. did you mean use if (ret) or drop the test altogether? There's no standard style for regmap functions so I used what was used in the rest of this driver. Mark? > >> + return ret; >> + > >> + /* enable output x */ > > (1) > >> + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, >> + BIT(offset), BIT(offset)); >> + if (ret < 0) >> + return ret; >> + >> + /* set value */ > > (2) > > With this (1)->(2) ordering it may be a glitch. So, first set value (if > hardware allows you, otherwise it seems like a broken one), and then switch > it to output. good suggestion, thanks. > >> + return regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, >> + BIT(offset), value << offset); > > You are using many times BIT(offset) mask above, perhaps > int mask = BIT(offset); > > Also, more robust is to use ternary here: 'value ? BIT(offset) : 0'. > Rationale: think what happen with value != 1 (theoretical possibility in the > future). ok > >> +} > > ... > >> +static int pcm512x_gpio_get(struct gpio_chip *chip, unsigned int offset) >> +{ > >> + return (val >> offset) & 1; > > Don't forget to use BIT() macro. > > return !!(val & BIT(offset)); There's a point where this becomes less readable IMHO, but fine. The !! gives me a headache... >> +static void pcm512x_gpio_set(struct gpio_chip *chip, unsigned int offset, >> + int value) >> +{ >> + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); >> + int ret; >> + >> + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, >> + BIT(offset), value << offset); > > value ? BIT(offset) : 0 ok > >> + if (ret < 0) > >> + pr_debug("%s: regmap_update_bits failed: %d\n", __func__, ret); > > No __func__ in debug messages. > Use dev_dbg() when we have struct device available. Not sure we do, will look into this. >> +static const struct gpio_chip template_chip = { > > Give better name, please. E.g. pcm512x_gpio_chip. ok >> + /* expose 6 GPIO pins, numbered from 1 to 6 */ >> + pcm512x->chip = template_chip; >> + pcm512x->chip.parent = dev; >> + >> + ret = devm_gpiochip_add_data(dev, &pcm512x->chip, pcm512x); > >> + if (ret != 0) { > > if (ret) ok
On Tue, Apr 14, 2020 at 12:52:07PM -0500, Pierre-Louis Bossart wrote: ... > > > +static int pcm512x_gpio_direction_output(struct gpio_chip *chip, > > > + unsigned int offset, > > > + int value) > > > +{ > > > + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); > > > + unsigned int reg; > > > + int ret; > > > + > > > + /* select Register GPIOx output for OUTPUT_x (1..6) */ > > > + reg = PCM512x_GPIO_OUTPUT_1 + offset; > > > > > + ret = regmap_update_bits(pcm512x->regmap, reg, 0x0f, 0x02); > > > > Magic numbers detected. > > > > > + if (ret < 0) > > > > Drop unnecessary ' < 0' parts where it makes sense, like here. > > did you mean use if (ret) or drop the test altogether? Do you see 'ret' part in my phrase above? > There's no standard style for regmap functions so I used what was used in > the rest of this driver. I see. May be than to drop it from the rest and do not add more? > > > + return ret; ... > > > + return (val >> offset) & 1; > > > > Don't forget to use BIT() macro. > > > > return !!(val & BIT(offset)); > > There's a point where this becomes less readable IMHO, but fine. > The !! gives me a headache... You can check assembly if it gives better result in code generation. But at least new drivers in GPIO are using it. ... > > > + pr_debug("%s: regmap_update_bits failed: %d\n", __func__, ret); > > > > No __func__ in debug messages. > > Use dev_dbg() when we have struct device available. > > Not sure we do, will look into this. I didn't get you in the first part. Are you talking about struct device? It's there, just needs to be de-referenced from GPIO chip.
On Tue, Apr 14, 2020 at 7:09 PM Andy Shevchenko <andriy.shevchenko@linux.intel.com> wrote: > On Thu, Apr 09, 2020 at 02:58:26PM -0500, Pierre-Louis Bossart wrote: > > The GPIOs are used e.g. on HifiBerry DAC+ HATs to control the LED > > (GPIO3) and the choice of the 44.1 (GPIO6) or 48 (GPIO3) kHz > > oscillator (when present). > > > > Enable basic gpio_chip to get/set values and get/set > > directions. Tested with GPIO_LIB from sys/class/gpio, the LED turns > > on/off as desired. > > > One question, can this use existing GPIO infrastructure, like bgpio_init()? > Ah, I see, that one operates over MMIO, while we would need something based on > regmap API. > > Bartosz, do we have plans to have bgpio_regmap_init() or alike? Michael Walle is working on that: https://lore.kernel.org/linux-gpio/20200402203656.27047-11-michael@walle.cc/ I think we should try to merge it sooner rather than later. I can provide an ib-* branch for ASoC whenever we agreed on a basic generic driver. Yours, Linus Walleij
On 4/16/20 6:42 AM, Linus Walleij wrote: > On Tue, Apr 14, 2020 at 7:09 PM Andy Shevchenko > <andriy.shevchenko@linux.intel.com> wrote: >> On Thu, Apr 09, 2020 at 02:58:26PM -0500, Pierre-Louis Bossart wrote: >>> The GPIOs are used e.g. on HifiBerry DAC+ HATs to control the LED >>> (GPIO3) and the choice of the 44.1 (GPIO6) or 48 (GPIO3) kHz >>> oscillator (when present). >>> >>> Enable basic gpio_chip to get/set values and get/set >>> directions. Tested with GPIO_LIB from sys/class/gpio, the LED turns >>> on/off as desired. >> >> >> One question, can this use existing GPIO infrastructure, like bgpio_init()? >> Ah, I see, that one operates over MMIO, while we would need something based on >> regmap API. >> >> Bartosz, do we have plans to have bgpio_regmap_init() or alike? > > Michael Walle is working on that: > https://lore.kernel.org/linux-gpio/20200402203656.27047-11-michael@walle.cc/ > > I think we should try to merge it sooner rather than later. > I can provide an ib-* branch for ASoC whenever we agreed > on a basic generic driver. Thanks for the pointer, I will give it a try.
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 4cbef9affffd..4f895a588c31 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -10,6 +10,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/clk.h> +#include <linux/gpio/driver.h> #include <linux/kernel.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -32,6 +33,7 @@ static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { struct pcm512x_priv { struct regmap *regmap; struct clk *sclk; + struct gpio_chip chip; struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; int fmt; @@ -1503,6 +1505,102 @@ const struct regmap_config pcm512x_regmap = { }; EXPORT_SYMBOL_GPL(pcm512x_regmap); +static int pcm512x_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(pcm512x->regmap, PCM512x_GPIO_EN, &val); + if (ret < 0) + return ret; + + val = (val >> offset) & 1; + + /* val is 0 for input, 1 for output, return inverted */ + return val ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; +} + +static int pcm512x_gpio_direction_input(struct gpio_chip *chip, + unsigned int offset) +{ + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); + + return regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, + BIT(offset), 0); +} + +static int pcm512x_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, + int value) +{ + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); + unsigned int reg; + int ret; + + /* select Register GPIOx output for OUTPUT_x (1..6) */ + reg = PCM512x_GPIO_OUTPUT_1 + offset; + ret = regmap_update_bits(pcm512x->regmap, reg, 0x0f, 0x02); + if (ret < 0) + return ret; + + /* enable output x */ + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_EN, + BIT(offset), BIT(offset)); + if (ret < 0) + return ret; + + /* set value */ + return regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, + BIT(offset), value << offset); +} + +static int pcm512x_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); + unsigned int val; + int ret; + + ret = regmap_read(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, &val); + if (ret < 0) + return ret; + + return (val >> offset) & 1; +} + +static void pcm512x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct pcm512x_priv *pcm512x = gpiochip_get_data(chip); + int ret; + + ret = regmap_update_bits(pcm512x->regmap, PCM512x_GPIO_CONTROL_1, + BIT(offset), value << offset); + + if (ret < 0) + pr_debug("%s: regmap_update_bits failed: %d\n", __func__, ret); +} + +/* list human-readable names, makes GPIOLIB usage straightforward */ +static const char * const pcm512x_gpio_names[] = { + "PCM512x-GPIO1", "PCM512x-GPIO2", "PCM512x-GPIO3", + "PCM512x-GPIO4", "PCM512x-GPIO5", "PCM512x-GPIO6" +}; + +static const struct gpio_chip template_chip = { + .label = "pcm512x-gpio", + .names = pcm512x_gpio_names, + .owner = THIS_MODULE, + .get_direction = pcm512x_gpio_get_direction, + .direction_input = pcm512x_gpio_direction_input, + .direction_output = pcm512x_gpio_direction_output, + .get = pcm512x_gpio_get, + .set = pcm512x_gpio_set, + .base = -1, /* let gpiolib select the base */ + .ngpio = ARRAY_SIZE(pcm512x_gpio_names), +}; + int pcm512x_probe(struct device *dev, struct regmap *regmap) { struct pcm512x_priv *pcm512x; @@ -1563,6 +1661,16 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) goto err; } + /* expose 6 GPIO pins, numbered from 1 to 6 */ + pcm512x->chip = template_chip; + pcm512x->chip.parent = dev; + + ret = devm_gpiochip_add_data(dev, &pcm512x->chip, pcm512x); + if (ret != 0) { + dev_err(dev, "Failed to register gpio chip: %d\n", ret); + goto err; + } + pcm512x->sclk = devm_clk_get(dev, NULL); if (PTR_ERR(pcm512x->sclk) == -EPROBE_DEFER) { ret = -EPROBE_DEFER;
The GPIOs are used e.g. on HifiBerry DAC+ HATs to control the LED (GPIO3) and the choice of the 44.1 (GPIO6) or 48 (GPIO3) kHz oscillator (when present). Enable basic gpio_chip to get/set values and get/set directions. Tested with GPIO_LIB from sys/class/gpio, the LED turns on/off as desired. Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> --- sound/soc/codecs/pcm512x.c | 108 +++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+)