On 05/04/17 17:44, Fabrice Gasnier wrote: > On 04/02/2017 02:21 PM, Jonathan Cameron wrote: >> On 02/04/17 12:45, Jonathan Cameron wrote: >>> On 31/03/17 12:45, Fabrice Gasnier wrote: >>>> STM32 DAC supports triggers to synchronize conversions. When trigger >>>> occurs, data is transferred from DHR (data holding register) to DOR >>>> (data output register) so output voltage is updated. >>>> Both hardware and software triggers are supported. >>>> >>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@xxxxxx> >>> Hmm. This is a somewhat different use of triggered event from normal... >>> > Waveform generator in STM32 DAC requires a trigger to increment / > decrement internal counter in case of triangle generator. Noise > generator is a bit different, but same trigger usage applies. I agree > this is unusual. > Is it acceptable to use event trigger for this use ? Not sure. It's kind of a like an output trigger but with the buffer tied to a hardware source (a bit like the hardware consumers we have in the other direction). I think it's closer to an output trigger than an event trigger certainly. Question is whether it should be something else entirely... > >>> What you have here is rather closer to the output buffers stuff that Analog >>> have in their tree which hasn't made it upstream yet. >>> To that end I'll want Lars to have a look at this... I've completely >>> lost track of where they are with this. >>> Perhaps Lars can give us a quick update? >>> >>> If that was in place (or what I have in my head was true anyway), >>> it would look like the reverse of the triggered buffer input devices. >>> You'd be able to write to a software buffer and it would clock them >>> out as the trigger fires (here I think it would have to keep updating >>> the DHR whenever the trigger occurs). > > Hmm.. for waveform generator mode, there is no need for data buffer. DAC > generate samples itself, using trigger. But, i agree it would be nice > for playing data samples (write DHR registers, or dma), yes. Definitely in the nice to have category - except wrt to having a hardware_buffer_provider or similar to provide the data stream. This is kind of similar to when we have a data pipeline on incoming data where the data is never actually visible to some IIO device because it's pushed into some processing engine directly. It's a bit of an oddity that we need to think about when looking at output triggering. The previous DDS devices I have seen have all be directly clocked... > >>> >>> Even if it's not there, we aren't necessarily looking at terribly big job >>> to implement it in the core and that would make this handling a lot more >>> 'standard' and consistent. >> >> Having tracked down some limited docs (AN3126 - Audio and waveform >> generation using the DAC in STM32 microcontrollers) the fact this >> can also be driven by DMA definitely argues in favour of working with >> Analog on getting the output buffers support upstream. >> >> *crosses fingers people have the time!* > > Hopefully this can happen. > > For the time being, I'll propose a similar patch in V2. I found out this > patch is missing a clear path to (re-)assign trigger, once set by > userland. Also, driver never gets informed in case trigger gets changed > or removed, without re-enabling it: > e.g. like echo "" > trigger/current_trigger > I'll propose a small change. Hope you agree with this approach. > Cool. Definitely looking for some analog devices input on this. They have a quite a few similar out of tree drivers beyond those currently in staging... Michael / Lars? I briefly discussed output buffers with Lars earlier in the week and the main outstanding issues were around userspace ABI. Last time we talked about this in detail was quite a few years ago IIRC so time for a revisit. + hopefully some progress. Jonathan > Thanks, > Fabrice > >>> >>> Jonathan >>> >>>> --- >>>> drivers/iio/dac/Kconfig | 3 + >>>> drivers/iio/dac/stm32-dac-core.h | 12 ++++ >>>> drivers/iio/dac/stm32-dac.c | 124 ++++++++++++++++++++++++++++++++++++++- >>>> 3 files changed, 136 insertions(+), 3 deletions(-) >>>> >>>> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig >>>> index 7198648..786c38b 100644 >>>> --- a/drivers/iio/dac/Kconfig >>>> +++ b/drivers/iio/dac/Kconfig >>>> @@ -278,6 +278,9 @@ config STM32_DAC >>>> tristate "STMicroelectronics STM32 DAC" >>>> depends on (ARCH_STM32 && OF) || COMPILE_TEST >>>> depends on REGULATOR >>>> + select IIO_TRIGGERED_EVENT >>>> + select IIO_STM32_TIMER_TRIGGER >>>> + select MFD_STM32_TIMERS >>>> select STM32_DAC_CORE >>>> help >>>> Say yes here to build support for STMicroelectronics STM32 Digital >>>> diff --git a/drivers/iio/dac/stm32-dac-core.h b/drivers/iio/dac/stm32-dac-core.h >>>> index d3099f7..3bf211c 100644 >>>> --- a/drivers/iio/dac/stm32-dac-core.h >>>> +++ b/drivers/iio/dac/stm32-dac-core.h >>>> @@ -26,6 +26,7 @@ >>>> >>>> /* STM32 DAC registers */ >>>> #define STM32_DAC_CR 0x00 >>>> +#define STM32_DAC_SWTRIGR 0x04 >>>> #define STM32_DAC_DHR12R1 0x08 >>>> #define STM32_DAC_DHR12R2 0x14 >>>> #define STM32_DAC_DOR1 0x2C >>>> @@ -33,8 +34,19 @@ >>>> >>>> /* STM32_DAC_CR bit fields */ >>>> #define STM32_DAC_CR_EN1 BIT(0) >>>> +#define STM32H7_DAC_CR_TEN1 BIT(1) >>>> +#define STM32H7_DAC_CR_TSEL1_SHIFT 2 >>>> +#define STM32H7_DAC_CR_TSEL1 GENMASK(5, 2) >>>> +#define STM32_DAC_CR_WAVE1 GENMASK(7, 6) >>>> +#define STM32_DAC_CR_MAMP1 GENMASK(11, 8) >>>> #define STM32H7_DAC_CR_HFSEL BIT(15) >>>> #define STM32_DAC_CR_EN2 BIT(16) >>>> +#define STM32_DAC_CR_WAVE2 GENMASK(23, 22) >>>> +#define STM32_DAC_CR_MAMP2 GENMASK(27, 24) >>>> + >>>> +/* STM32_DAC_SWTRIGR bit fields */ >>>> +#define STM32_DAC_SWTRIGR_SWTRIG1 BIT(0) >>>> +#define STM32_DAC_SWTRIGR_SWTRIG2 BIT(1) >>>> >>>> /** >>>> * struct stm32_dac_common - stm32 DAC driver common data (for all instances) >>>> diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c >>>> index ee9711d..62e43e9 100644 >>>> --- a/drivers/iio/dac/stm32-dac.c >>>> +++ b/drivers/iio/dac/stm32-dac.c >>>> @@ -23,6 +23,10 @@ >>>> #include <linux/bitfield.h> >>>> #include <linux/delay.h> >>>> #include <linux/iio/iio.h> >>>> +#include <linux/iio/timer/stm32-timer-trigger.h> >>>> +#include <linux/iio/trigger.h> >>>> +#include <linux/iio/trigger_consumer.h> >>>> +#include <linux/iio/triggered_event.h> >>>> #include <linux/kernel.h> >>>> #include <linux/module.h> >>>> #include <linux/platform_device.h> >>>> @@ -31,15 +35,112 @@ >>>> >>>> #define STM32_DAC_CHANNEL_1 1 >>>> #define STM32_DAC_CHANNEL_2 2 >>>> +/* channel2 shift */ >>>> +#define STM32_DAC_CHAN2_SHIFT 16 >>>> >>>> /** >>>> * struct stm32_dac - private data of DAC driver >>>> * @common: reference to DAC common data >>>> + * @swtrig: Using software trigger >>>> */ >>>> struct stm32_dac { >>>> struct stm32_dac_common *common; >>>> + bool swtrig; >>>> }; >>>> >>>> +/** >>>> + * struct stm32_dac_trig_info - DAC trigger info >>>> + * @name: name of the trigger, corresponding to its source >>>> + * @tsel: trigger selection, value to be configured in DAC_CR.TSELx >>>> + */ >>>> +struct stm32_dac_trig_info { >>>> + const char *name; >>>> + u32 tsel; >>>> +}; >>>> + >>>> +static const struct stm32_dac_trig_info stm32h7_dac_trinfo[] = { >>>> + { "swtrig", 0 }, >>>> + { TIM1_TRGO, 1 }, >>>> + { TIM2_TRGO, 2 }, >>>> + { TIM4_TRGO, 3 }, >>>> + { TIM5_TRGO, 4 }, >>>> + { TIM6_TRGO, 5 }, >>>> + { TIM7_TRGO, 6 }, >>>> + { TIM8_TRGO, 7 }, >>>> + {}, >>>> +}; >>>> + >>>> +static irqreturn_t stm32_dac_trigger_handler(int irq, void *p) >>>> +{ >>>> + struct iio_poll_func *pf = p; >>>> + struct iio_dev *indio_dev = pf->indio_dev; >>>> + struct stm32_dac *dac = iio_priv(indio_dev); >>>> + int channel = indio_dev->channels[0].channel; >>>> + >>>> + /* Using software trigger? Then, trigger it now */ >>>> + if (dac->swtrig) { >>>> + u32 swtrig; >>>> + >>>> + if (channel == STM32_DAC_CHANNEL_1) >>>> + swtrig = STM32_DAC_SWTRIGR_SWTRIG1; >>>> + else >>>> + swtrig = STM32_DAC_SWTRIGR_SWTRIG2; >>>> + regmap_update_bits(dac->common->regmap, STM32_DAC_SWTRIGR, >>>> + swtrig, swtrig); >>>> + } >>>> + >>>> + iio_trigger_notify_done(indio_dev->trig); >>>> + >>>> + return IRQ_HANDLED; >>>> +} >>>> + >>>> +static unsigned int stm32_dac_get_trig_tsel(struct stm32_dac *dac, >>>> + struct iio_trigger *trig) >>>> +{ >>>> + unsigned int i; >>>> + >>>> + /* skip 1st trigger that should be swtrig */ >>>> + for (i = 1; stm32h7_dac_trinfo[i].name; i++) { >>>> + /* >>>> + * Checking both stm32 timer trigger type and trig name >>>> + * should be safe against arbitrary trigger names. >>>> + */ >>>> + if (is_stm32_timer_trigger(trig) && >>>> + !strcmp(stm32h7_dac_trinfo[i].name, trig->name)) { >>>> + return stm32h7_dac_trinfo[i].tsel; >>>> + } >>>> + } >>>> + >>>> + /* When no trigger has been found, default to software trigger */ >>>> + dac->swtrig = true; >>>> + >>>> + return stm32h7_dac_trinfo[0].tsel; >>>> +} >>>> + >>>> +static int stm32_dac_set_trig(struct stm32_dac *dac, struct iio_trigger *trig, >>>> + int channel) >>>> +{ >>>> + struct iio_dev *indio_dev = iio_priv_to_dev(dac); >>>> + u32 shift = channel == STM32_DAC_CHANNEL_1 ? 0 : STM32_DAC_CHAN2_SHIFT; >>>> + u32 val = 0, tsel; >>>> + u32 msk = (STM32H7_DAC_CR_TEN1 | STM32H7_DAC_CR_TSEL1) << shift; >>>> + >>>> + dac->swtrig = false; >>>> + if (trig) { >>>> + /* select & enable trigger (tsel / ten) */ >>>> + tsel = stm32_dac_get_trig_tsel(dac, trig); >>>> + val = tsel << STM32H7_DAC_CR_TSEL1_SHIFT; >>>> + val = (val | STM32H7_DAC_CR_TEN1) << shift; >>>> + } >>>> + >>>> + if (trig) >>>> + dev_dbg(&indio_dev->dev, "enable trigger: %s\n", trig->name); >>>> + else >>>> + dev_dbg(&indio_dev->dev, "disable trigger\n"); >>>> + >>>> + return regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, val); >>>> +} >>>> + >>>> static int stm32_dac_is_enabled(struct stm32_dac *dac, int channel) >>>> { >>>> u32 en, val; >>>> @@ -63,9 +164,16 @@ static int stm32_dac_enable(struct iio_dev *indio_dev, int channel) >>>> STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2; >>>> int ret; >>>> >>>> + ret = stm32_dac_set_trig(dac, indio_dev->trig, channel); >>>> + if (ret < 0) { >>>> + dev_err(&indio_dev->dev, "Trigger setup failed\n"); >>>> + return ret; >>>> + } >>>> + >>>> ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, en, en); >>>> if (ret < 0) { >>>> dev_err(&indio_dev->dev, "Enable failed\n"); >>>> + stm32_dac_set_trig(dac, NULL, channel); >>>> return ret; >>>> } >>>> >>>> @@ -88,10 +196,12 @@ static int stm32_dac_disable(struct iio_dev *indio_dev, int channel) >>>> int ret; >>>> >>>> ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, en, 0); >>>> - if (ret) >>>> + if (ret) { >>>> dev_err(&indio_dev->dev, "Disable failed\n"); >>>> + return ret; >>>> + } >>>> >>>> - return ret; >>>> + return stm32_dac_set_trig(dac, NULL, channel); >>>> } >>>> >>>> static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val) >>>> @@ -258,10 +368,17 @@ static int stm32_dac_probe(struct platform_device *pdev) >>>> if (ret < 0) >>>> return ret; >>>> >>>> - ret = iio_device_register(indio_dev); >>>> + ret = iio_triggered_event_setup(indio_dev, NULL, >>>> + stm32_dac_trigger_handler); >>>> if (ret) >>>> return ret; >>>> >>>> + ret = iio_device_register(indio_dev); >>>> + if (ret) { >>>> + iio_triggered_event_cleanup(indio_dev); >>>> + return ret; >>>> + } >>>> + >>>> return 0; >>>> } >>>> >>>> @@ -269,6 +386,7 @@ static int stm32_dac_remove(struct platform_device *pdev) >>>> { >>>> struct iio_dev *indio_dev = platform_get_drvdata(pdev); >>>> >>>> + iio_triggered_event_cleanup(indio_dev); >>>> iio_device_unregister(indio_dev); >>>> >>>> return 0; >>>> >>> >>> -- >>> To unsubscribe from this list: send the line "unsubscribe linux-iio" 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-iio" 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 devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html