[PATCH] ARM: at91/dt: Fix sama5d3x typos.
From c23d712fe924b929c2eb39eba644fe74bcccfd37 Mon Sep 17 00:00:00 2001 From: Peter Rosin p...@axentia.se Date: Thu, 23 Oct 2014 13:52:03 +0200 Subject: [PATCH] ARM: at91/dt: Fix sama5d3x typos. The DT compatible strings also need binding documentation, but that is for someone else to write. Signed-off-by: Peter Rosin p...@axentia.se --- arch/arm/boot/dts/sama5d31.dtsi |2 +- arch/arm/boot/dts/sama5d33.dtsi |2 +- arch/arm/boot/dts/sama5d34.dtsi |2 +- arch/arm/boot/dts/sama5d35.dtsi |2 +- arch/arm/boot/dts/sama5d36.dtsi |2 +- arch/arm/boot/dts/sama5d3xcm.dtsi |2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/arch/arm/boot/dts/sama5d31.dtsi b/arch/arm/boot/dts/sama5d31.dtsi index 7997dc9..883878b 100644 --- a/arch/arm/boot/dts/sama5d31.dtsi +++ b/arch/arm/boot/dts/sama5d31.dtsi @@ -12,5 +12,5 @@ #include sama5d3_uart.dtsi / { - compatible = atmel,samad31, atmel,sama5d3, atmel,sama5; + compatible = atmel,sama5d31, atmel,sama5d3, atmel,sama5; }; diff --git a/arch/arm/boot/dts/sama5d33.dtsi b/arch/arm/boot/dts/sama5d33.dtsi index 39f8322..4b4434a 100644 --- a/arch/arm/boot/dts/sama5d33.dtsi +++ b/arch/arm/boot/dts/sama5d33.dtsi @@ -10,5 +10,5 @@ #include sama5d3_gmac.dtsi / { - compatible = atmel,samad33, atmel,sama5d3, atmel,sama5; + compatible = atmel,sama5d33, atmel,sama5d3, atmel,sama5; }; diff --git a/arch/arm/boot/dts/sama5d34.dtsi b/arch/arm/boot/dts/sama5d34.dtsi index 89cda2c..aa01573 100644 --- a/arch/arm/boot/dts/sama5d34.dtsi +++ b/arch/arm/boot/dts/sama5d34.dtsi @@ -12,5 +12,5 @@ #include sama5d3_mci2.dtsi / { - compatible = atmel,samad34, atmel,sama5d3, atmel,sama5; + compatible = atmel,sama5d34, atmel,sama5d3, atmel,sama5; }; diff --git a/arch/arm/boot/dts/sama5d35.dtsi b/arch/arm/boot/dts/sama5d35.dtsi index d20cd71..16c39f4 100644 --- a/arch/arm/boot/dts/sama5d35.dtsi +++ b/arch/arm/boot/dts/sama5d35.dtsi @@ -14,5 +14,5 @@ #include sama5d3_tcb1.dtsi / { - compatible = atmel,samad35, atmel,sama5d3, atmel,sama5; + compatible = atmel,sama5d35, atmel,sama5d3, atmel,sama5; }; diff --git a/arch/arm/boot/dts/sama5d36.dtsi b/arch/arm/boot/dts/sama5d36.dtsi index db58cad..e85139e 100644 --- a/arch/arm/boot/dts/sama5d36.dtsi +++ b/arch/arm/boot/dts/sama5d36.dtsi @@ -16,5 +16,5 @@ #include sama5d3_uart.dtsi / { - compatible = atmel,samad36, atmel,sama5d3, atmel,sama5; + compatible = atmel,sama5d36, atmel,sama5d3, atmel,sama5; }; diff --git a/arch/arm/boot/dts/sama5d3xcm.dtsi b/arch/arm/boot/dts/sama5d3xcm.dtsi index f7d8583..56574c8 100644 --- a/arch/arm/boot/dts/sama5d3xcm.dtsi +++ b/arch/arm/boot/dts/sama5d3xcm.dtsi @@ -8,7 +8,7 @@ */ / { - compatible = atmel,samad3xcm, atmel,sama5d3, atmel,sama5; + compatible = atmel,sama5d3xcm, atmel,sama5d3, atmel,sama5; chosen { bootargs = console=ttyS0,115200 rootfstype=ubifs ubi.mtd=5 root=ubi0:rootfs; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v2] ASoC: atmel_ssc_dai: Match the CMR divider only in full duplex.
Hi Peter, Thanks for your patch. And thanks for the Ack! Btw, do you use git send-email command to send the patch? No, I didn't, git format-patch and paste into the mail body. Was there some whitespace issues with the patch? Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v2] ASoC: atmel_ssc_dai: Match the CMR divider only in full duplex.
Ok, I'm trying with git send-email, sorry for the inconvenience. Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ASoC: atmel_ssc_dai: Match the CMR divider only in full duplex.
The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in half-duplex scenarios, which is needed when using the OSS API. Thus, only lock the divider if there is a stream in the other direction. Fixes the below program to not fail with the atmel ssc dai in master mode. #include sys/ioctl.h #include unistd.h #include fcntl.h #include sys/soundcard.h int main(void) { int fd; int format; int channels; int speed; if ((fd = open(/dev/dsp, O_WRONLY, 0)) == -1) { perror(open); return 1; } format = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, format) == -1) { perror(SNDCTL_DSP_SETFMT); return 1; } channels = 2; if (ioctl(fd, SNDCTL_DSP_CHANNELS, channels) == -1) { perror(SNDCTL_DSP_CHANNELS); return 1; } speed = 22025; if (ioctl(fd, SNDCTL_DSP_SPEED, speed) == -1) { perror(SNDCTL_DSP_SPEED); return 1; } return 0; } Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f403f39..b1cc2a4 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, * transmit and receive, so if a value has already * been set, it must match this value. */ - if (ssc_p-cmr_div == 0) + if (ssc_p-dir_mask != + (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) + ssc_p-cmr_div = div; + else if (ssc_p-cmr_div == 0) ssc_p-cmr_div = div; else if (div != ssc_p-cmr_div) -- 2.1.1 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
From 1e5621d7b9887c648d1a66238dc82d715c1e2cad Mon Sep 17 00:00:00 2001 From: Peter Rosin p...@axentia.se Date: Mon, 20 Oct 2014 14:38:04 +0200 Subject: [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately. The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in a half-duplex scenario, which is needed when changing sample rates. Thus, keep track of the desired playback and capture clock dividers separately, and allow changing rates without closing the stream. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 31 ++- sound/soc/atmel/atmel_ssc_dai.h | 10 ++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f403f39..fec14fb 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -277,7 +277,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, /* Reset the SSC */ ssc_writel(ssc_p-ssc-regs, CR, SSC_BIT(CR_SWRST)); /* Clear the SSC dividers */ - ssc_p-cmr_div = ssc_p-tcmr_period = ssc_p-rcmr_period = 0; + ssc_p-tcmr_div = ssc_p-rcmr_div = 0; + ssc_p-tcmr_period = ssc_p-rcmr_period = 0; } spin_unlock_irq(ssc_p-lock); } @@ -304,17 +305,27 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, struct atmel_ssc_info *ssc_p = ssc_info[cpu_dai-id]; switch (div_id) { - case ATMEL_SSC_CMR_DIV: + case ATMEL_SSC_TCMR_DIV: /* * The same master clock divider is used for both * transmit and receive, so if a value has already -* been set, it must match this value. +* been set for the other direction, it must match +* this value. */ - if (ssc_p-cmr_div == 0) - ssc_p-cmr_div = div; - else - if (div != ssc_p-cmr_div) - return -EBUSY; + if (ssc_p-rcmr_div == 0) + ssc_p-tcmr_div = div; + else if (div != ssc_p-rcmr_div) + return -EBUSY; + break; + + case ATMEL_SSC_RCMR_DIV: + /* +* See ATMEL_SSC_TCMR_DIV. +*/ + if (ssc_p-tcmr_div == 0) + ssc_p-rcmr_div = div; + else if (div != ssc_p-tcmr_div) + return -EBUSY; break; case ATMEL_SSC_TCMR_PERIOD: @@ -345,6 +356,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; + u16 cmr; int start_event; int ret; int fslen, fslen_ext; @@ -626,7 +638,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, } /* set SSC clock mode register */ - ssc_writel(ssc_p-ssc-regs, CMR, ssc_p-cmr_div); + cmr = ssc_p-tcmr_div ? ssc_p-tcmr_div : ssc_p-rcmr_div; + ssc_writel(ssc_p-ssc-regs, CMR, cmr); /* set receive clock mode and format */ ssc_writel(ssc_p-ssc-regs, RCMR, rcmr); diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index b1f08d5..a25df7a 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h @@ -39,9 +39,10 @@ #define ATMEL_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */ /* SSC divider ids */ -#define ATMEL_SSC_CMR_DIV 0 /* MCK divider for BCLK */ -#define ATMEL_SSC_TCMR_PERIOD 1 /* BCLK divider for transmit FS */ -#define ATMEL_SSC_RCMR_PERIOD 2 /* BCLK divider for receive FS */ +#define ATMEL_SSC_TCMR_DIV 0 /* MCK divider for transmit BCLK */ +#define ATMEL_SSC_RCMR_DIV 1 /* MCK divider for receive BCLK */ +#define ATMEL_SSC_TCMR_PERIOD 2 /* BCLK divider for transmit FS */ +#define ATMEL_SSC_RCMR_PERIOD 3 /* BCLK divider for receive FS */ /* * SSC direction masks */ @@ -110,7 +111,8 @@ struct atmel_ssc_info { unsigned short dir_mask;/* 0=unused, 1=playback, 2=capture */ unsigned short initialized; /* true if SSC has been initialized */ unsigned short daifmt; - unsigned short cmr_div; + unsigned short tcmr_div; + unsigned short rcmr_div; unsigned short tcmr_period; unsigned short rcmr_period; struct atmel_pcm_dma_params *dma_params[2]; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message
RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
Hi! (and thank you for the pointer to the example with the ssc-dai in master mode) Hi Peter, On 10/20/2014 09:45 PM, Peter Rosin wrote: From 1e5621d7b9887c648d1a66238dc82d715c1e2cad Mon Sep 17 00:00:00 2001 From: Peter Rosin p...@axentia.se Date: Mon, 20 Oct 2014 14:38:04 +0200 Subject: [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately. The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in a half-duplex scenario, which is needed when changing sample rates. I am not fully get what you mean here, do you mean: - when playback, first playback 48kHz, and then playback 8Khz, the 8Khz still playback in 48kHz mode? or other things? I do not know exactly why the problem occurs, but it might be connected to the library (proprietary) we are using. It apparently uses the OSS interface and somehow it opens a stream and changes the playback sample-rate a couple of time before it settles on something. I don't know why this is done, and I have no control over it. The end result is that without this patch, the ssc-dai driver returns -EBUSY when the library changes the playback sample-rate (the driver returns -EBUSY because it thinks that the new sample rate does not match a previously given capture sample-rate, but that is of course bogus when no capture is going on at all). Thus, keep track of the desired playback and capture clock dividers separately, and allow changing rates without closing the stream. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 31 ++-- --- sound/soc/atmel/atmel_ssc_dai.h | 10 ++ 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f403f39..fec14fb 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -277,7 +277,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream, /* Reset the SSC */ ssc_writel(ssc_p-ssc-regs, CR, SSC_BIT(CR_SWRST)); /* Clear the SSC dividers */ - ssc_p-cmr_div = ssc_p-tcmr_period = ssc_p-rcmr_period = 0; + ssc_p-tcmr_div = ssc_p-rcmr_div = 0; + ssc_p-tcmr_period = ssc_p-rcmr_period = 0; } spin_unlock_irq(ssc_p-lock); } @@ -304,17 +305,27 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, struct atmel_ssc_info *ssc_p = ssc_info[cpu_dai-id]; switch (div_id) { - case ATMEL_SSC_CMR_DIV: + case ATMEL_SSC_TCMR_DIV: /* * The same master clock divider is used for both * transmit and receive, so if a value has already -* been set, it must match this value. +* been set for the other direction, it must match +* this value. */ - if (ssc_p-cmr_div == 0) - ssc_p-cmr_div = div; - else - if (div != ssc_p-cmr_div) - return -EBUSY; + if (ssc_p-rcmr_div == 0) + ssc_p-tcmr_div = div; + else if (div != ssc_p-rcmr_div) + return -EBUSY; + break; + + case ATMEL_SSC_RCMR_DIV: + /* +* See ATMEL_SSC_TCMR_DIV. +*/ + if (ssc_p-tcmr_div == 0) + ssc_p-rcmr_div = div; + else if (div != ssc_p-tcmr_div) + return -EBUSY; break; case ATMEL_SSC_TCMR_PERIOD: @@ -345,6 +356,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, struct atmel_pcm_dma_params *dma_params; int dir, channels, bits; u32 tfmr, rfmr, tcmr, rcmr; + u16 cmr; should be u32. Ok, I'll send an updated patch later. int start_event; int ret; int fslen, fslen_ext; @@ -626,7 +638,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, } /* set SSC clock mode register */ - ssc_writel(ssc_p-ssc-regs, CMR, ssc_p-cmr_div); + cmr = ssc_p-tcmr_div ? ssc_p-tcmr_div : ssc_p-rcmr_div; + ssc_writel(ssc_p-ssc-regs, CMR, cmr); /* set receive clock mode and format */ ssc_writel(ssc_p-ssc-regs, RCMR, rcmr); diff --git a/sound/soc/atmel/atmel_ssc_dai.h b/sound/soc/atmel/atmel_ssc_dai.h index b1f08d5..a25df7a 100644 --- a/sound/soc/atmel/atmel_ssc_dai.h +++ b/sound/soc/atmel/atmel_ssc_dai.h @@ -39,9 +39,10 @@ #define ATMEL_SYSCLK_MCK 0 /* SSC uses AT91 MCK as system clock */ /* SSC divider ids */ -#define ATMEL_SSC_CMR_DIV 0 /* MCK divider for BCLK */ -#define
RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
Hi again! Hi Peter, On 10/21/2014 03:55 PM, Peter Rosin wrote: Hi! (and thank you for the pointer to the example with the ssc-dai in master mode) Hi Peter, On 10/20/2014 09:45 PM, Peter Rosin wrote: From 1e5621d7b9887c648d1a66238dc82d715c1e2cad Mon Sep 17 00:00:00 2001 From: Peter Rosin p...@axentia.se Date: Mon, 20 Oct 2014 14:38:04 +0200 Subject: [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately. The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in a half-duplex scenario, which is needed when changing sample rates. I am not fully get what you mean here, do you mean: - when playback, first playback 48kHz, and then playback 8Khz, the 8Khz still playback in 48kHz mode? or other things? I do not know exactly why the problem occurs, but it might be connected to the library (proprietary) we are using. It apparently uses the OSS interface and somehow it opens a stream and changes the playback sample-rate a couple of time before it settles on something. I don't know why this is done, and I have no control over it. The end result is that without this patch, the ssc-dai driver returns -EBUSY when the library changes the playback sample-rate (the driver returns -EBUSY because it thinks that the new sample rate does not match a previously given capture sample-rate, but that is of course bogus when no capture is going on at all). If this issue is caused by your proprietary library, please fix in the library. Ah, but it's not our proprietary library to fix, it's simply a library that we are using (speech synthesis, not our area of expertise). So, I'm not in a position to simply fix it, as I have no source code. I don't know how this code can fix your issue and also there is no improvement to the code and it absolutely increase work (choose which clock to configure: tcmr or rcmr) for the SSC work in master. And also this patch may confuse the end user, let them thinking the clock for tcmr and rcmr can configure seperately. I added some traces to our hw_params and got the following (without the patch): [ 161.17] atmel ssc startup [ 161.17] TFA9879: axentia_asoc_tfa9879_hw_params - mclk rate 6600 [ 161.18] TFA9879: axentia_asoc_tfa9879_hw_params - bclk rate 128000 [ 161.18] TFA9879: axentia_asoc_tfa9879_hw_params - bclk divider 128 [ 161.19] TFA9879: axentia_asoc_tfa9879_hw_params - period 7 [ 161.20] TFA9879: axentia_asoc_tfa9879_hw_params - mclk rate 6600 [ 161.20] TFA9879: axentia_asoc_tfa9879_hw_params - bclk rate 128000 [ 161.21] TFA9879: axentia_asoc_tfa9879_hw_params - bclk divider 128 [ 161.22] TFA9879: axentia_asoc_tfa9879_hw_params - period 7 [ 161.23] TFA9879: axentia_asoc_tfa9879_hw_params - mclk rate 6600 [ 161.23] TFA9879: axentia_asoc_tfa9879_hw_params - bclk rate 256000 [ 161.24] TFA9879: axentia_asoc_tfa9879_hw_params - bclk divider 64 [ 161.25] TFA9879: axentia_asoc_tfa9879_hw_params - Failed to set cpu dai clk divider [ 161.26] axentia-tfa9879-audio sound.9: ASoC: machine hw_params failed: -16 So, I think keep the code as is (without this patch applied), what's your opinion? In that case we'll simply carry the patch ourselves. I don't know why the above happens, or if it is caused by bad behavior in the library, or what. But I do know that the library is unusable for us without some action. I thought it was an improvement, and therefore sent the patch... Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
Hi again, -Original Message- From: Peter Rosin Sent: Tuesday, October 21, 2014 13:05 To: 'Bo Shen' Cc: Liam Girdwood; Mark Brown; Jaroslav Kysela; Takashi Iwai; 'alsa- de...@alsa-project.org'; linux-kernel@vger.kernel.org Subject: RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately. Hi again! Hi Peter, On 10/21/2014 03:55 PM, Peter Rosin wrote: Hi! (and thank you for the pointer to the example with the ssc-dai in master mode) Hi Peter, On 10/20/2014 09:45 PM, Peter Rosin wrote: From 1e5621d7b9887c648d1a66238dc82d715c1e2cad Mon Sep 17 00:00:00 2001 From: Peter Rosin p...@axentia.se Date: Mon, 20 Oct 2014 14:38:04 +0200 Subject: [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately. The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in a half-duplex scenario, which is needed when changing sample rates. I am not fully get what you mean here, do you mean: - when playback, first playback 48kHz, and then playback 8Khz, the 8Khz still playback in 48kHz mode? or other things? I do not know exactly why the problem occurs, but it might be connected to the library (proprietary) we are using. It apparently uses the OSS interface and somehow it opens a stream and changes the playback sample-rate a couple of time before it settles on something. I don't know why this is done, and I have no control over it. The end result is that without this patch, the ssc-dai driver returns -EBUSY when the library changes the playback sample-rate (the driver returns -EBUSY because it thinks that the new sample rate does not match a previously given capture sample-rate, but that is of course bogus when no capture is going on at all). If this issue is caused by your proprietary library, please fix in the library. Ah, but it's not our proprietary library to fix, it's simply a library that we are using (speech synthesis, not our area of expertise). So, I'm not in a position to simply fix it, as I have no source code. I don't know how this code can fix your issue and also there is no improvement to the code and it absolutely increase work (choose which clock to configure: tcmr or rcmr) for the SSC work in master. And also this patch may confuse the end user, let them thinking the clock for tcmr and rcmr can configure seperately. I added some traces to our hw_params and got the following (without the patch): [ 161.17] atmel ssc startup [ 161.17] TFA9879: axentia_asoc_tfa9879_hw_params - mclk rate 6600 [ 161.18] TFA9879: axentia_asoc_tfa9879_hw_params - bclk rate 128000 [ 161.18] TFA9879: axentia_asoc_tfa9879_hw_params - bclk divider 128 [ 161.19] TFA9879: axentia_asoc_tfa9879_hw_params - period 7 [ 161.20] TFA9879: axentia_asoc_tfa9879_hw_params - mclk rate 6600 [ 161.20] TFA9879: axentia_asoc_tfa9879_hw_params - bclk rate 128000 [ 161.21] TFA9879: axentia_asoc_tfa9879_hw_params - bclk divider 128 [ 161.22] TFA9879: axentia_asoc_tfa9879_hw_params - period 7 [ 161.23] TFA9879: axentia_asoc_tfa9879_hw_params - mclk rate 6600 [ 161.23] TFA9879: axentia_asoc_tfa9879_hw_params - bclk rate 256000 [ 161.24] TFA9879: axentia_asoc_tfa9879_hw_params - bclk divider 64 [ 161.25] TFA9879: axentia_asoc_tfa9879_hw_params - Failed to set cpu dai clk divider [ 161.26] axentia-tfa9879-audio sound.9: ASoC: machine hw_params failed: -16 I did some further tests, and the following program fails without the patch: #include sys/ioctl.h #include unistd.h #include fcntl.h #include sys/soundcard.h int main(void) { int fd; int format; int channels; if ((fd = open(/dev/dsp, O_WRONLY, 0)) == -1) { perror(open); return 1; } format = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, format) == -1) { perror(SNDCTL_DSP_SETFMT); return 1; } channels = 2; if (ioctl(fd, SNDCTL_DSP_CHANNELS, channels) == -1) { perror(SNDCTL_DSP_CHANNELS); return 1; } return 0; } Output: SNDCTL_DSP_CHANNELS: Device or resource busy (I admin to having edited the above code slightly in this mail, so I might have introduced some silly bug, but you get what I mean, just open the device and request some parameters, and boom: -EBUSY) Cheers, Peter So, I think keep the code as is (without this patch applied), what's your opinion? In that case we'll simply carry the patch ourselves. I don't know why the above happens
RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
Hi! Hi Peter, On 10/21/2014 09:05 PM, Peter Rosin wrote: I did some further tests, and the following program fails without the patch: With the patch, it is OK? Yes. #include sys/ioctl.h #include unistd.h #include fcntl.h #include sys/soundcard.h int main(void) { int fd; int format; int channels; if ((fd = open(/dev/dsp, O_WRONLY, 0)) == -1) { perror(open); return 1; } format = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, format) == -1) { perror(SNDCTL_DSP_SETFMT); return 1; } channels = 2; if (ioctl(fd, SNDCTL_DSP_CHANNELS, channels) == -1) { perror(SNDCTL_DSP_CHANNELS); return 1; } return 0; } Output: SNDCTL_DSP_CHANNELS: Device or resource busy This return from codec or from atmel_ssc_dai? This -EBUSY definitely comes from atmel_ssc_set_dai_sysclk, when my card-driver tries to set ATMEL_SSC_CMR_DIV. With the patch, it works. (the codec is spdif-transmitter, since the i2c interface of the actual tfa9879 codec is not directly reachable from the linux cpu, but that has nothing to do with this issue). (I admin to having edited the above code slightly in this mail, so I s/admin/admit/ might have introduced some silly bug, but you get what I mean, just open the device and request some parameters, and boom: -EBUSY) Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
Hi! With the patch, it is OK? Yes. #include sys/ioctl.h #include unistd.h #include fcntl.h #include sys/soundcard.h int main(void) { int fd; int format; int channels; if ((fd = open(/dev/dsp, O_WRONLY, 0)) == -1) { perror(open); return 1; } format = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, format) == -1) { perror(SNDCTL_DSP_SETFMT); return 1; } channels = 2; if (ioctl(fd, SNDCTL_DSP_CHANNELS, channels) == -1) { perror(SNDCTL_DSP_CHANNELS); return 1; } return 0; } Output: SNDCTL_DSP_CHANNELS: Device or resource busy This return from codec or from atmel_ssc_dai? This -EBUSY definitely comes from atmel_ssc_set_dai_sysclk, when my card-driver tries to set ATMEL_SSC_CMR_DIV. With the patch, it works. (the codec is spdif-transmitter, since the i2c interface of the actual tfa9879 codec is not directly reachable from the linux cpu, but that has nothing to do with this issue). I try to reproduce it (using the code your pasted directly) on atmel sama5d3xek with wm8904 code, don't meet this error. I also go through the OSS code, I still don't find this is related with atmel_ssc_set_dai_sysclk. So, am I missing something or something else? The sama5d3xek/wm9804 combo, as implemented in the kernel, has the ssc dai in slave mode, and therefore don't need to fiddle with any ssc dai dividers (atmel_9804.c :atmel_asoc_wm9804_hw_params() only sets things in the wm9804 codec dai driver and leaves the ssc dai to itself). Instead, try the above code on your code with the ssc dai in master mode that you pointed at previously. https://github.com/Android4SAM/linux-at91/commit/33db8ebd3e75632c482dda271340f4d2adcfd320 If that happens to not hit -EBUSY (which it might not, since the wm9804 codec will only allow stereo, so the SNDCTL_DSP_CHANNELS ioctl might not need to make any change for any ssc divider) add code to also set a non-default sample rate, e.g.: speed = 22050; if (ioctl(fd, SNDCTL_DSP_SPEED, speed) == -1) { perror(SNDCTL_DSP_SPEED); return 1; } Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [alsa-devel] [PATCH] ASoC: atmel_ssc_dai: Track playback and capture CMR dividers separately.
Bo Chen wrote: with this piece of code, I reproduce your issue. Now, I know the reason of this issue, work in oss mode, it will set the default clock to 8KHz, and then if change to other sample rate, for example 48KHz, the div is different, then it reports -EBUSY. Indeed. So, I think we won't change the ATMEL_SSC_CMR_DIV to ATMEL_SSC_TCMR_DIV and ATMEL_SSC_RCMR_DIV, as it will affect other users. We just deal with this situation in ATMEL_SSC_CMR_DIV block, check the direction, if the same direction change the div, then accept the change, otherwise, return -EBUSY. Ok. But I'm not sure it is possible to dig out the current direction in the .set_clkdiv callback? Perhaps the correct fix is to set the bits .symmetric_rates, .symmetric_channels and .symmetric_samplebits in the atmel_ssc_dai struct when the ssc dai is master? Then I expect that other mechanisms will kick in that will render the current CMR_DIV check pointless? Is a dai driver allowed to change these symmetry bits after registration? Can they be set in the .set_sysclk callback? Perhaps in the ATMEL_SSC_CMR_DIV block itself? That callback should only be called when the dai is master, so that would be perfect... Yes, the limitation would be a little bit more strict than today, but is it really common to require different modes on playback and capture? Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2] ASoC: atmel_ssc_dai: Match the CMR divider only in full duplex.
From 86be84c4de4e7b21cfda9656a02a902c543210af Mon Sep 17 00:00:00 2001 From: Peter Rosin p...@axentia.se Date: Wed, 22 Oct 2014 16:45:29 +0200 Subject: [PATCH v2] ASoC: atmel_ssc_dai: Match the CMR divider only in full duplex. The CMR divider register is shared by playback and capture. The SSC driver therefore tries to enforce rules so that the needed register content do not conflict during simultaneous playback/capture. However, the implementation also prevents changing the register content in half-duplex scenarios, which is needed when using the OSS API. Thus, only lock the divider if there is a stream in the other direction. Fixes the below program to not fail with the atmel ssc dai in master mode. #include sys/ioctl.h #include unistd.h #include fcntl.h #include sys/soundcard.h int main(void) { int fd; int format; int channels; int speed; if ((fd = open(/dev/dsp, O_WRONLY, 0)) == -1) { perror(open); return 1; } format = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, format) == -1) { perror(SNDCTL_DSP_SETFMT); return 1; } channels = 2; if (ioctl(fd, SNDCTL_DSP_CHANNELS, channels) == -1) { perror(SNDCTL_DSP_CHANNELS); return 1; } speed = 22025; if (ioctl(fd, SNDCTL_DSP_SPEED, speed) == -1) { perror(SNDCTL_DSP_SPEED); return 1; } return 0; } Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c |5 - 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index de433cfd..9ae8475 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, * transmit and receive, so if a value has already * been set, it must match this value. */ - if (ssc_p-cmr_div == 0) + if (ssc_p-dir_mask != + (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE)) + ssc_p-cmr_div = div; + else if (ssc_p-cmr_div == 0) ssc_p-cmr_div = div; else if (div != ssc_p-cmr_div) -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH 1/2] ASoC: tfa9879: New driver for NXP Semiconductors TFA9879 amplifier.
Thanks for the review! I'm answering here, but would like to thank Lars-Peter for the review as well. Mark Brown wrote: On Thu, Nov 06, 2014 at 01:54:00PM +0100, Peter Rosin wrote: +#define TFA9879_REG(codec, reg, field, value) \ + snd_soc_update_bits(codec, TFA9879_ ## reg, \ + TFA9879_ ## field ## _MASK, \ + (value) TFA9879_ ## field ## _SHIFT) Please don't do stuff like this, it just makes the code look more obscure than it should be for people who work with multiple devices - just use update_bits() directly. Ok, I'll zap it. Pity though, I really liked the actual code even if the macro was a bit hard to digest... +static int tfa9879_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + + TFA9879_REG(codec, DEVICE_CONTROL, POWERUP, 1); Use DAPM to manage power, you're probably looking for a supply widget. I'll try to find out how to hook that up. BTW, there is very limited info about the supply widget at http://www.alsa-project.org/main/index.php/DAPM + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: Use params_width() rather than a specific memory layout. Ok. + if (tfa9879-lsb_justified) + TFA9879_REG(codec, SERIAL_INTERFACE_1, I2S_SET, i2s_set); Why does this need to be reset every time, shouldn't we just be setting the register in set_fmt().? Yes, I'd sure like to do that, but how do I get to the width in set_fmt()? +static int tfa9879_probe(struct snd_soc_codec *codec) { + struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec); + + codec-control_data = tfa9879-regmap; This is redundant, just remove the entire function - ASoC will get the regmap automatically. Oops, sorry. Forward-porting in progress... + { TFA9879_MISC_STATUS, 0x }, /* 0x15, read-only */ +}; + +static bool tfa9879_volatile_register(struct device *dev, unsigned +int reg) { + return reg == TFA9879_MISC_STATUS; If the register is volatile it shouldn't have a default value provided. Then I misunderstood what volatile was meant to do. I'll just nuke the function. It works fine anyway... I'll send a v2 later, with the other bits from Lars-Peter taken care of, and with 2/2 squashed. Need to test first though... Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2] ASoC: tfa9879: New driver for NXP Semiconductors TFA9879 amplifier.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- MAINTAINERS|6 + sound/soc/codecs/Kconfig |5 + sound/soc/codecs/Makefile |2 + sound/soc/codecs/tfa9879.c | 323 sound/soc/codecs/tfa9879.h | 202 +++ 5 files changed, 538 insertions(+) create mode 100644 sound/soc/codecs/tfa9879.c create mode 100644 sound/soc/codecs/tfa9879.h diff --git a/MAINTAINERS b/MAINTAINERS index f10ed3914ea8..9ca9e68ea9ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6467,6 +6467,12 @@ S: Supported F: drivers/gpu/drm/i2c/tda998x_drv.c F: include/drm/i2c/tda998x.h +NXP TFA9879 DRIVER +M: Peter Rosin p...@axentia.se +L: alsa-de...@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/codecs/tfa9879* + OMAP SUPPORT M: Tony Lindgren t...@atomide.com L: linux-o...@vger.kernel.org diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8838838e25ed..36bf3f83a333 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -96,6 +96,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -548,6 +549,10 @@ config SND_SOC_TAS5086 tristate Texas Instruments TAS5086 speaker amplifier depends on I2C +config SND_SOC_TFA9879 + tristate NXP Semiconductors TFA9879 amplifier + depends on I2C + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20afe0f0c5be..678a3a6df8a5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -96,6 +96,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -264,6 +265,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c new file mode 100644 index ..c48022639a29 --- /dev/null +++ b/sound/soc/codecs/tfa9879.c @@ -0,0 +1,323 @@ +/* + * tfa9879.c -- driver for NXP Semiconductors TFA9879 + * + * Copyright (C) 2014 Axentia Technologies AB + * Author: Peter Rosin p...@axentia.se + * + * 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. + * + */ + +#include linux/module.h +#include linux/init.h +#include linux/i2c.h +#include linux/regmap.h +#include sound/soc.h +#include sound/tlv.h +#include sound/pcm_params.h + +#include tfa9879.h + +struct tfa9879_priv { + struct regmap *regmap; + int lsb_justified; +}; + +static int tfa9879_hw_params(struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params, +struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec); + int fs; + int i2s_set = 0; + + switch (params_rate(params)) { + case 8000: + fs = TFA9879_I2S_FS_8000; + break; + case 11025: + fs = TFA9879_I2S_FS_11025; + break; + case 12000: + fs = TFA9879_I2S_FS_12000; + break; + case 16000: + fs = TFA9879_I2S_FS_16000; + break; + case 22050: + fs = TFA9879_I2S_FS_22050; + break; + case 24000: + fs = TFA9879_I2S_FS_24000; + break; + case 32000: + fs = TFA9879_I2S_FS_32000; + break; + case 44100: + fs = TFA9879_I2S_FS_44100; + break; + case 48000: + fs = TFA9879_I2S_FS_48000; + break; + case 64000: + fs = TFA9879_I2S_FS_64000; + break; + case 88200: + fs = TFA9879_I2S_FS_88200
[v2] NXP Semiconductors TFA9879 Amplifier Driver
On 2014-11-06 17:02, Mark Brown wrote: On Thu, Nov 06, 2014 at 02:37:31PM +, Peter Rosin wrote: Mark Brown wrote: +if (tfa9879-lsb_justified) +TFA9879_REG(codec, SERIAL_INTERFACE_1, I2S_SET, i2s_set); Why does this need to be reset every time, shouldn't we just be setting the register in set_fmt().? Yes, I'd sure like to do that, but how do I get to the width in set_fmt()? Oh, this has some width related thing in it? Yes, the amp has a different setting for each lsb-justified width. (It also supports 18 and 20 bits wide data) +{ TFA9879_MISC_STATUS, 0x }, /* 0x15, read-only */ +}; +static bool tfa9879_volatile_register(struct device *dev, unsigned +int reg) { +return reg == TFA9879_MISC_STATUS; If the register is volatile it shouldn't have a default value provided. Then I misunderstood what volatile was meant to do. I'll just nuke the function. It works fine anyway... A volatile register is one that the chip may change autonomously (eg, an interrupt status register). That was what I assumed, and the register behaves like that. I naively thought that declaring it as volatile would prevent the asoc core from writing to it. In retrospect, I don't understand why I thought that... Anyway, that bit can wait until someone actually needs to read the staus. Here's an update with the following changes since v1: - squashed patch 2/2 - zapped the TFA9879_REG macro - zapped tfa9879_probe (which needlessly registered the regmap) - moved tfa9879_prepare/_shutdown to a DAPM_SUPPLY widget - zapped the tfa9879_volatile() thing - made tfa9879_dai_ops const - erased the redundant Gain from the bass/treble volume controls - using params_width() instead of params_format() Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
NXP Semiconductors TFA9879 Amplifier Driver
Mark Brown wrote: On Thu, Nov 06, 2014 at 05:39:45PM +0100, Peter Rosin wrote: + { TFA9879_MISC_STATUS, 0x }, /* 0x15, read-only */ The fix here is the wrong way round - if the device is reporting status here there should be no default and there should be a volatile operation (though it's not the end of the world to omit that if it's not used). Otherwise this looks good. Ok, I think I finally see what you mean... Diff since v2 below. Cheers, Peter diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index c48022639a29..16f1b71edb55 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -182,9 +182,13 @@ static struct reg_default tfa9879_regs[] = { { TFA9879_HIGH_PASS_FILTER, 0x0004 }, /* 0x12 */ { TFA9879_VOLUME_CONTROL, 0x10bd }, /* 0x13 */ { TFA9879_MISC_CONTROL, 0x }, /* 0x14 */ - { TFA9879_MISC_STATUS, 0x }, /* 0x15, read-only */ }; +static bool tfa9879_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TFA9879_MISC_STATUS; +} + static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1); static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0); static const char * const tb_freq_text[] = { @@ -240,6 +244,7 @@ static const struct regmap_config tfa9879_regmap = { .reg_bits = 8, .val_bits = 16, + .volatile_reg = tfa9879_volatile_reg, .max_register = TFA9879_MISC_STATUS, .reg_defaults = tfa9879_regs, .num_reg_defaults = ARRAY_SIZE(tfa9879_regs), @@ -285,7 +290,7 @@ static int tfa9879_i2c_probe(struct i2c_client *i2c, return PTR_ERR(tfa9879-regmap); /* Ensure the device is in reset state */ - for (i = 0; i ARRAY_SIZE(tfa9879_regs) - 1; i++) + for (i = 0; i ARRAY_SIZE(tfa9879_regs); i++) regmap_write(tfa9879-regmap, tfa9879_regs[i].reg, tfa9879_regs[i].def); -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v3] ASoC: tfa9879: New driver for NXP Semiconductors TFA9879 amplifier.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- MAINTAINERS|6 + sound/soc/codecs/Kconfig |5 + sound/soc/codecs/Makefile |2 + sound/soc/codecs/tfa9879.c | 328 sound/soc/codecs/tfa9879.h | 202 +++ 5 files changed, 543 insertions(+) create mode 100644 sound/soc/codecs/tfa9879.c create mode 100644 sound/soc/codecs/tfa9879.h diff --git a/MAINTAINERS b/MAINTAINERS index f10ed3914ea8..9ca9e68ea9ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6467,6 +6467,12 @@ S: Supported F: drivers/gpu/drm/i2c/tda998x_drv.c F: include/drm/i2c/tda998x.h +NXP TFA9879 DRIVER +M: Peter Rosin p...@axentia.se +L: alsa-de...@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/codecs/tfa9879* + OMAP SUPPORT M: Tony Lindgren t...@atomide.com L: linux-o...@vger.kernel.org diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8838838e25ed..36bf3f83a333 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -96,6 +96,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -548,6 +549,10 @@ config SND_SOC_TAS5086 tristate Texas Instruments TAS5086 speaker amplifier depends on I2C +config SND_SOC_TFA9879 + tristate NXP Semiconductors TFA9879 amplifier + depends on I2C + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20afe0f0c5be..678a3a6df8a5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -96,6 +96,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -264,6 +265,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c new file mode 100644 index ..16f1b71edb55 --- /dev/null +++ b/sound/soc/codecs/tfa9879.c @@ -0,0 +1,328 @@ +/* + * tfa9879.c -- driver for NXP Semiconductors TFA9879 + * + * Copyright (C) 2014 Axentia Technologies AB + * Author: Peter Rosin p...@axentia.se + * + * 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. + * + */ + +#include linux/module.h +#include linux/init.h +#include linux/i2c.h +#include linux/regmap.h +#include sound/soc.h +#include sound/tlv.h +#include sound/pcm_params.h + +#include tfa9879.h + +struct tfa9879_priv { + struct regmap *regmap; + int lsb_justified; +}; + +static int tfa9879_hw_params(struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params, +struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec); + int fs; + int i2s_set = 0; + + switch (params_rate(params)) { + case 8000: + fs = TFA9879_I2S_FS_8000; + break; + case 11025: + fs = TFA9879_I2S_FS_11025; + break; + case 12000: + fs = TFA9879_I2S_FS_12000; + break; + case 16000: + fs = TFA9879_I2S_FS_16000; + break; + case 22050: + fs = TFA9879_I2S_FS_22050; + break; + case 24000: + fs = TFA9879_I2S_FS_24000; + break; + case 32000: + fs = TFA9879_I2S_FS_32000; + break; + case 44100: + fs = TFA9879_I2S_FS_44100; + break; + case 48000: + fs = TFA9879_I2S_FS_48000; + break; + case 64000: + fs = TFA9879_I2S_FS_64000; + break; + case 88200: + fs = TFA9879_I2S_FS_88200
[PATCH] pm: at91: Workaround DDRSDRC self-refresh bug with LPDDR1 memories
From: Peter Rosin p...@axentia.se The DDRSDR controller (on the ATSAMA5D31) fails miserably to put LPDDR1 memories in self-refresh. Force the controller to think it has DDR2 memories during the self-refresh period, as the DDR2 self-refresh spec is equivalent to LPDDR1, and is correctly implemented in the controller. Assume that the second controller has the same fault, and that other CPUs in the family has the same problem, but that is untested. Signed-off-by: Peter Rosin p...@axentia.se --- arch/arm/mach-at91/pm_slowclock.S | 43 +++- include/soc/at91/at91sam9_ddrsdr.h |2 +- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index 20018779bae7..63a9e0b0a17d 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S @@ -143,6 +143,16 @@ ddr_sr_enable: cmp memctrl, #AT91_MEMCTRL_DDRSDR bne sdr_sr_enable + /* LPDDR1 -- force DDR2 mode during self-refresh */ + ldr tmp1, [sdramc, #AT91_DDRSDRC_MDR] + str tmp1, .saved_sam9_mdr + bic tmp1, tmp1, #~AT91_DDRSDRC_MD + cmp tmp1, #AT91_DDRSDRC_MD_LOW_POWER_DDR + ldreq tmp1, [sdramc, #AT91_DDRSDRC_MDR] + biceq tmp1, tmp1, #AT91_DDRSDRC_MD + orreq tmp1, tmp1, #AT91_DDRSDRC_MD_DDR2 + streq tmp1, [sdramc, #AT91_DDRSDRC_MDR] + /* prepare for DDRAM self-refresh mode */ ldr tmp1, [sdramc, #AT91_DDRSDRC_LPR] str tmp1, .saved_sam9_lpr @@ -151,14 +161,26 @@ ddr_sr_enable: /* figure out if we use the second ram controller */ cmp ramc1, #0 - ldrne tmp2, [ramc1, #AT91_DDRSDRC_LPR] - strne tmp2, .saved_sam9_lpr1 - bicne tmp2, #AT91_DDRSDRC_LPCB - orrne tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH + beq ddr_no_2nd_ctrl + + ldr tmp2, [ramc1, #AT91_DDRSDRC_MDR] + str tmp2, .saved_sam9_mdr1 + bic tmp2, tmp2, #~AT91_DDRSDRC_MD + cmp tmp2, #AT91_DDRSDRC_MD_LOW_POWER_DDR + ldreq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + biceq tmp2, tmp2, #AT91_DDRSDRC_MD + orreq tmp2, tmp2, #AT91_DDRSDRC_MD_DDR2 + streq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + + ldr tmp2, [ramc1, #AT91_DDRSDRC_LPR] + str tmp2, .saved_sam9_lpr1 + bic tmp2, #AT91_DDRSDRC_LPCB + orr tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH /* Enable DDRAM self-refresh mode */ + str tmp2, [ramc1, #AT91_DDRSDRC_LPR] +ddr_no_2nd_ctrl: str tmp1, [sdramc, #AT91_DDRSDRC_LPR] - strne tmp2, [ramc1, #AT91_DDRSDRC_LPR] b sdr_sr_done @@ -289,12 +311,17 @@ sdr_sr_done: */ cmp memctrl, #AT91_MEMCTRL_DDRSDR bne sdr_en_restore + /* Restore MDR in case of LPDDR1 */ + ldr tmp1, .saved_sam9_mdr + str tmp1, [sdramc, #AT91_DDRSDRC_MDR] /* Restore LPR on AT91 with DDRAM */ ldr tmp1, .saved_sam9_lpr str tmp1, [sdramc, #AT91_DDRSDRC_LPR] /* if we use the second ram controller */ cmp ramc1, #0 + ldrne tmp2, .saved_sam9_mdr1 + strne tmp2, [ramc1, #AT91_DDRSDRC_MDR] ldrne tmp2, .saved_sam9_lpr1 strne tmp2, [ramc1, #AT91_DDRSDRC_LPR] @@ -328,5 +355,11 @@ ram_restored: .saved_sam9_lpr1: .word 0 +.saved_sam9_mdr: + .word 0 + +.saved_sam9_mdr1: + .word 0 + ENTRY(at91_slow_clock_sz) .word .-at91_slow_clock diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h index 0210797abf2e..cd2c18787833 100644 --- a/include/soc/at91/at91sam9_ddrsdr.h +++ b/include/soc/at91/at91sam9_ddrsdr.h @@ -92,7 +92,7 @@ #defineAT91_DDRSDRC_UPD_MR (3 20)/* Update load mode register and extended mode register */ #define AT91_DDRSDRC_MDR 0x20/* Memory Device Register */ -#defineAT91_DDRSDRC_MD (3 0)/* Memory Device Type */ +#defineAT91_DDRSDRC_MD (7 0)/* Memory Device Type */ #defineAT91_DDRSDRC_MD_SDR 0 #defineAT91_DDRSDRC_MD_LOW_POWER_SDR 1 #defineAT91_DDRSDRC_MD_LOW_POWER_DDR 3 -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
NXP Semiconductors TFA9879 Amplifier Driver
Hi! Sorry for not sending this from my axentia.se account, but I tend to get high spam-scores from there when I use git send-email. This is a new driver, and it's pretty minimalistic with support for only a few basic controls. However, it is usable and I'd be happy to see it included. I don't know if it's presumptious to add myself as a maintainer, but checkpatch complained... Are the alsa control names wisely chosen? Please review! Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 1/2] ASoC: tfa9879: New driver for NXP Semiconductors TFA9879 amplifier.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- MAINTAINERS|6 + sound/soc/codecs/Kconfig |5 + sound/soc/codecs/Makefile |2 + sound/soc/codecs/tfa9879.c | 334 sound/soc/codecs/tfa9879.h | 202 +++ 5 files changed, 549 insertions(+) create mode 100644 sound/soc/codecs/tfa9879.c create mode 100644 sound/soc/codecs/tfa9879.h diff --git a/MAINTAINERS b/MAINTAINERS index f10ed3914ea8..9ca9e68ea9ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6467,6 +6467,12 @@ S: Supported F: drivers/gpu/drm/i2c/tda998x_drv.c F: include/drm/i2c/tda998x.h +NXP TFA9879 DRIVER +M: Peter Rosin p...@axentia.se +L: alsa-de...@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: sound/soc/codecs/tfa9879* + OMAP SUPPORT M: Tony Lindgren t...@atomide.com L: linux-o...@vger.kernel.org diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8838838e25ed..36bf3f83a333 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -96,6 +96,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_STAC9766 if SND_SOC_AC97_BUS select SND_SOC_TAS2552 if I2C select SND_SOC_TAS5086 if I2C + select SND_SOC_TFA9879 if I2C select SND_SOC_TLV320AIC23_I2C if I2C select SND_SOC_TLV320AIC23_SPI if SPI_MASTER select SND_SOC_TLV320AIC26 if SPI_MASTER @@ -548,6 +549,10 @@ config SND_SOC_TAS5086 tristate Texas Instruments TAS5086 speaker amplifier depends on I2C +config SND_SOC_TFA9879 + tristate NXP Semiconductors TFA9879 amplifier + depends on I2C + config SND_SOC_TLV320AIC23 tristate diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20afe0f0c5be..678a3a6df8a5 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -96,6 +96,7 @@ snd-soc-sta350-objs := sta350.o snd-soc-sta529-objs := sta529.o snd-soc-stac9766-objs := stac9766.o snd-soc-tas5086-objs := tas5086.o +snd-soc-tfa9879-objs := tfa9879.o snd-soc-tlv320aic23-objs := tlv320aic23.o snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o @@ -264,6 +265,7 @@ obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o +obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o obj-$(CONFIG_SND_SOC_TLV320AIC23) += snd-soc-tlv320aic23.o obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C) += snd-soc-tlv320aic23-i2c.o obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI) += snd-soc-tlv320aic23-spi.o diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c new file mode 100644 index ..90cc28f7e6ed --- /dev/null +++ b/sound/soc/codecs/tfa9879.c @@ -0,0 +1,334 @@ +/* + * tfa9879.c -- driver for NXP Semiconductors TFA9879 + * + * Copyright (C) 2014 Axentia Technologies AB + * Author: Peter Rosin p...@axentia.se + * + * 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. + * + */ + +#include linux/module.h +#include linux/init.h +#include linux/i2c.h +#include linux/regmap.h +#include sound/soc.h +#include sound/tlv.h +#include sound/pcm_params.h + +#include tfa9879.h + +struct tfa9879_priv { + struct regmap *regmap; + int lsb_justified; +}; + +#define TFA9879_REG(codec, reg, field, value) \ + snd_soc_update_bits(codec, TFA9879_ ## reg, \ + TFA9879_ ## field ## _MASK, \ + (value) TFA9879_ ## field ## _SHIFT) + +static int tfa9879_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + + TFA9879_REG(codec, DEVICE_CONTROL, POWERUP, 1); + + return 0; +} + +static int tfa9879_hw_params(struct snd_pcm_substream *substream, +struct snd_pcm_hw_params *params, +struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec); + int fs; + int i2s_set = 0; + + switch (params_rate(params)) { + case 8000: + fs = TFA9879_I2S_FS_8000; + break; + case 11025: + fs = TFA9879_I2S_FS_11025; + break; + case 12000: + fs = TFA9879_I2S_FS_12000; + break; + case 16000: + fs = TFA9879_I2S_FS_16000; + break; + case 22050: + fs
[PATCH 2/2] ASoC: tfa9879: Add bass and treble gain/freq controls.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/tfa9879.c | 16 1 file changed, 16 insertions(+) diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 90cc28f7e6ed..0d62962542e2 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -213,10 +213,26 @@ static bool tfa9879_volatile_register(struct device *dev, unsigned int reg) } static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1); +static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0); +static const char * const tb_freq_text[] = { + Low, Mid, High +}; +static const struct soc_enum treble_freq_enum = + SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT, + ARRAY_SIZE(tb_freq_text), tb_freq_text); +static const struct soc_enum bass_freq_enum = + SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT, + ARRAY_SIZE(tb_freq_text), tb_freq_text); static const struct snd_kcontrol_new tfa9879_controls[] = { SOC_SINGLE_TLV(PCM Playback Volume, TFA9879_VOLUME_CONTROL, TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv), + SOC_SINGLE_TLV(Treble Gain Volume, TFA9879_BASS_TREBLE, + TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv), + SOC_SINGLE_TLV(Bass Gain Volume, TFA9879_BASS_TREBLE, + TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv), + SOC_ENUM(Treble Corner Freq, treble_freq_enum), + SOC_ENUM(Bass Corner Freq, bass_freq_enum), }; static const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = { -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ASoC: Augment existing card DAPM routes in snd_soc_of_parse_audio_routing
From: Peter Rosin p...@axentia.se If a snd_soc_card has any DAPM routes when it calls snd_soc_of_parse_audio_routing, those are clobbered without this change. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/soc-core.c | 14 +- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b60ff56ebc0f..377db486852e 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -4585,7 +4585,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) { struct device_node *np = card-dev-of_node; - int num_routes; + int num_routes, old_routes; struct snd_soc_dapm_route *routes; int i, ret; @@ -4603,7 +4603,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } - routes = devm_kzalloc(card-dev, num_routes * sizeof(*routes), + old_routes = card-num_dapm_routes; + routes = devm_kzalloc(card-dev, + (old_routes + num_routes) * sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card-dev, @@ -4611,9 +4613,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } + memcpy(routes, card-dapm_routes, old_routes * sizeof(*routes)); + for (i = 0; i num_routes; i++) { ret = of_property_read_string_index(np, propname, - 2 * i, routes[i].sink); + 2 * i, routes[old_routes + i].sink); if (ret) { dev_err(card-dev, ASoC: Property '%s' index %d could not be read: %d\n, @@ -4621,7 +4625,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } ret = of_property_read_string_index(np, propname, - (2 * i) + 1, routes[i].source); + (2 * i) + 1, routes[old_routes + i].source); if (ret) { dev_err(card-dev, ASoC: Property '%s' index %d could not be read: %d\n, @@ -4630,7 +4634,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } } - card-num_dapm_routes = num_routes; + card-num_dapm_routes += num_routes; card-dapm_routes = routes; return 0; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH] ASoC: Augment existing card DAPM routes in snd_soc_of_parse_audio_routing
Mark Brown wrote: On Thu, Nov 27, 2014 at 10:02:42PM +0100, Peter Rosin wrote: - routes = devm_kzalloc(card-dev, num_routes * sizeof(*routes), + old_routes = card-num_dapm_routes; + routes = devm_kzalloc(card-dev, + (old_routes + num_routes) * sizeof(*routes), GFP_KERNEL); if (!routes) { dev_err(card-dev, @@ -4611,9 +4613,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, return -EINVAL; } + memcpy(routes, card-dapm_routes, old_routes * sizeof(*routes)); + Aren't we open coding krealloc() here? I don't think krealloc() is appropriate since we don't know where the memory comes from. The typical use I see is that the card struct is initialized as: static const struct snd_soc_dapm_route foo_intercon[] = { { MUX1, Loop, IN1 }, { MUX1, Mixer, MIX1 }, { MIX1, NULL, DAC }, { MIX1, IN1 Switch, IN1 }, { Line Out Jack, NULL, MUX1 }, }; static struct snd_soc_card foo_card = { .name = foo, .owner = THIS_MODULE, .dapm_routes = foo_intercon, .num_dapm_routes = ARRAY_SIZE(foo_intercon), /* etc */ }; If that's the case, krealloc() seems dead wrong. On the other hand, if snd_soc_of_parse_audio_routing() were to be called many times, a lot of devm_kzalloc()ed memory would be kept dangling. I don't expect a card to call snd_soc_of_parse_audio_routing() more than once though... Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ASoC: pcm512x: Trigger auto-increment of register addresses on i2c
From: Peter Rosin p...@axentia.se When the codec is connected using i2c, it will only auto-increment register addresses if msb (0x80) of the register address byte is set. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x-i2c.c |7 ++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 4d62230bd378..d0547fa275fc 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -24,8 +24,13 @@ static int pcm512x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct regmap *regmap; + struct regmap_config config = pcm512x_regmap; - regmap = devm_regmap_init_i2c(i2c, pcm512x_regmap); + /* msb needs to be set to enable auto-increment of addresses */ + config.read_flag_mask = 0x80; + config.write_flag_mask = 0x80; + + regmap = devm_regmap_init_i2c(i2c, config); if (IS_ERR(regmap)) return PTR_ERR(regmap); -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ASoC: pcm512x: Also support PCM514x devices
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- Documentation/devicetree/bindings/sound/pcm512x.txt |3 ++- sound/soc/codecs/pcm512x-i2c.c |4 sound/soc/codecs/pcm512x-spi.c |4 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index faff75e64573..98e0d34915e8 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -5,7 +5,8 @@ on the board). Required properties: - - compatible : One of ti,pcm5121 or ti,pcm5122 + - compatible : One of ti,pcm5121, ti,pcm5122, ti,pcm5141 or + ti,pcm5142 - reg : the I2C address of the device for I2C, the chip select number for SPI. diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 4d62230bd378..08047ac952f4 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -41,6 +41,8 @@ static int pcm512x_i2c_remove(struct i2c_client *i2c) static const struct i2c_device_id pcm512x_i2c_id[] = { { pcm5121, }, { pcm5122, }, + { pcm5141, }, + { pcm5142, }, { } }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); @@ -48,6 +50,8 @@ MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); static const struct of_device_id pcm512x_of_match[] = { { .compatible = ti,pcm5121, }, { .compatible = ti,pcm5122, }, + { .compatible = ti,pcm5141, }, + { .compatible = ti,pcm5142, }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); diff --git a/sound/soc/codecs/pcm512x-spi.c b/sound/soc/codecs/pcm512x-spi.c index f297058c0038..7b64a9cef704 100644 --- a/sound/soc/codecs/pcm512x-spi.c +++ b/sound/soc/codecs/pcm512x-spi.c @@ -43,6 +43,8 @@ static int pcm512x_spi_remove(struct spi_device *spi) static const struct spi_device_id pcm512x_spi_id[] = { { pcm5121, }, { pcm5122, }, + { pcm5141, }, + { pcm5142, }, { }, }; MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); @@ -50,6 +52,8 @@ MODULE_DEVICE_TABLE(spi, pcm512x_spi_id); static const struct of_device_id pcm512x_of_match[] = { { .compatible = ti,pcm5121, }, { .compatible = ti,pcm5122, }, + { .compatible = ti,pcm5141, }, + { .compatible = ti,pcm5142, }, { } }; MODULE_DEVICE_TABLE(of, pcm512x_of_match); -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v2 02/12] pm: at91: Workaround DDRSDRC self-refresh bug with LPDDR1 memories.
Sylvain Rochet wrote: Hello Nicolas, On Mon, Jan 26, 2015 at 02:34:38PM +0100, Nicolas Ferre wrote: Le 26/01/2015 11:36, Sylvain Rochet a écrit : I think we should explain we are dealing with an errata here, this is not obvious at first sight, the patch summary may find its place here :-) True but the problem is that this errata is not public yet, it will be in a couple of weeks. I have the feeling though that the commit message is pretty clear. We'll maybe add that its an actual errata. Humm, this is not what I meant actually. I only proposed a code source comment explaining why this is done this way, the current patch summary looked like it will be perfect between /* */ ;-) I did not want to fill up the source with wordy comments, and settled for a one-liner. I don't know much about the underlying reasons other than the fact that LPDDR1 mode of the controller isn't working properly in self-refresh and that the DDR2 spec is similar enough to work. The one-liner comment says about the same thing, but not with so many words. The comment does make it clear that the switch to DDR2 is intentional, and that is all that is needed as protection from some future cleanup. I mean, anyone seeing that comment and just erasing the whole thing without further investigation is not doing a very good job as there is no reason to intentionally switch from LPDDR1 mode to DDR2 mode, other that the fact that the LPDDR1 mode isn't working for some reason. That reason is not to be found in the commit message and I have no information to improve the situation. IMO, the only thing missing is a pointer to the as yet unreleased errata, which should explain the situation clearly for any and all interested parties. May I suggest that someone who cares sends a patch with the comment update when the errata is released? If others feel differently, by all means please reword and expand the comment. Cheers, Peter N�r��yb�X��ǧv�^�){.n�+{zX����ܨ}���Ơz�j:+v���zZ+��+zf���h���~i���z��w���?��)ߢf��^jǫy�m��@A�a��� 0��h���i
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Mark Brown wrote: On Wed, Feb 04, 2015 at 12:52:25PM +0100, Peter Rosin wrote: One thing remains a bit unclear, and that is the 500ppm deduction. Is that really warranted? The number was just pulled out of my hat... I don't really get what this is supposed to be protecting against. + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + t.min = 8000; + t.max = ssc_p-mck_rate / mck_div / frame_size; + /* Take away 500ppm, just to be on the safe side. */ + t.max -= t.max / 2000; + t.openmin = t.openmax = 0; + t.integer = 0; + ret = snd_interval_refine(i, t); As I understand it this is a straight divider rather than something that's doing dithering or anything else more fancy. Given that it seems as well just to trust the clock rate we've got - we don't do any error tracking with the clock API (perhaps we should) and for many applications some degree of divergence from the nominal rate is not *too* bad for audio systems (for application specific values of some and too of course). If it is just dividers I'm not sure the situation is really improved materially by knocking off the top frequency. If we are doing something more fancy than divididing my analysis is off base of course. I'm thinking that the SSC samples the selected BCK pin using the (possibly divided) peripheral clock. Getting too near the theoretical rate limit would be bad, if these two independent clocks drift the wrong way. At least that is my take on it, but I don't know the internal workings of the SSC, so... I was hoping that someone from Atmel could chime in? Maybe I'm totally off base, and the SSC is doing this completely differently? In our application, we're not near the limit. Therefore, it really doesn't matter much to us. Should I resend w/o the 500ppm deduction? Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
From: Peter Rosin p...@axentia.se When the SSC acts as BCK master, use a ratnum rule to limit the rate instead of only doing the standard rates. When the SSC acts as BCK slave, allow any BCK frequency up to within 500ppm of the SSC master clock, possibly divided by 2, 3 or 6. Put a cap at 384kHz. Who's /ever/ going to need more than that? The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio Considerations section from the SSC documentation: The Transmitter and the Receiver can be programmed to operate with the clock signals provided on either the TK or RK pins. This allows the SSC to support many slave-mode data transfers. In this case, the maximum clock speed allowed on the RK pin is: - Peripheral clock divided by 2 if Receiver Frame Synchro is input - Peripheral clock divided by 3 if Receiver Frame Synchro is output In addition, the maximum clock speed allowed on the TK pin is: - Peripheral clock divided by 6 if Transmit Frame Synchro is input - Peripheral clock divided by 2 if Transmit Frame Synchro is output Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 113 +-- sound/soc/atmel/atmel_ssc_dai.h |1 + 2 files changed, 110 insertions(+), 4 deletions(-) Changes since v1: - I have checked all Atmel datasheets I could get my hands on (and that seemed relevant), and in every instance where they have described the SSC, the description have the exact rate limitations on the RK and TK pin that I have implemented here. - Improved the comments. - Rebased on top of the latest patches from Bo Chen. One thing remains a bit unclear, and that is the 500ppm deduction. Is that really warranted? The number was just pulled out of my hat... Cheers, Peter diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 379ac2a6ab16..767f65bab25d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -187,6 +187,96 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * When the bit clock is input, limit the maximum rate according to the + * Serial Clock Ratio Considerations section from the SSC documentation: + * + * The Transmitter and the Receiver can be programmed to operate + * with the clock signals provided on either the TK or RK pins. + * This allows the SSC to support many slave-mode data transfers. + * In this case, the maximum clock speed allowed on the RK pin is: + * - Peripheral clock divided by 2 if Receiver Frame Synchro is input + * - Peripheral clock divided by 3 if Receiver Frame Synchro is output + * In addition, the maximum clock speed allowed on the TK pin is: + * - Peripheral clock divided by 6 if Transmit Frame Synchro is input + * - Peripheral clock divided by 2 if Transmit Frame Synchro is output + * + * When the bit clock is output, limit the rate according to the + * SSC divider restrictions. + */ +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct atmel_ssc_info *ssc_p = rule-private; + struct ssc_device *ssc = ssc_p-ssc; + struct snd_interval *i = hw_param_interval(params, rule-var); + struct snd_interval t; + struct snd_ratnum r = { + .den_min = 1, + .den_max = 4095, + .den_step = 1, + }; + unsigned int num = 0, den = 0; + int frame_size; + int mck_div = 2; + int ret; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size 0) + return frame_size; + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + if ((ssc_p-dir_mask SSC_DIR_MASK_CAPTURE) +ssc-clk_from_rk_pin) + /* Receiver Frame Synchro (i.e. capture) +* is output (format is _CFS) and the RK pin +* is used for input (format is _CBM_). +*/ + mck_div = 3; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + if ((ssc_p-dir_mask SSC_DIR_MASK_PLAYBACK) +!ssc-clk_from_rk_pin) + /* Transmit Frame Synchro (i.e. playback) +* is input (format is _CFM) and the TK pin +* is used for input (format _CBM_ but not +* using the RK pin). +*/ + mck_div = 6; + break; + } + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + r.num = ssc_p-mck_rate / mck_div / frame_size; + + ret = snd_interval_ratnum(i, 1, r, num, den); + if (ret = 0 den rule-var == SNDRV_PCM_HW_PARAM_RATE
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Bo Shen wrote: Hi Peter, Hi! On 02/07/2015 06:51 PM, Peter Rosin wrote: Mark Brown wrote: On Wed, Feb 04, 2015 at 12:52:25PM +0100, Peter Rosin wrote: One thing remains a bit unclear, and that is the 500ppm deduction. Is that really warranted? The number was just pulled out of my hat... I don't really get what this is supposed to be protecting against. + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + t.min = 8000; + t.max = ssc_p-mck_rate / mck_div / frame_size; + /* Take away 500ppm, just to be on the safe side. */ + t.max -= t.max / 2000; + t.openmin = t.openmax = 0; + t.integer = 0; + ret = snd_interval_refine(i, t); As I understand it this is a straight divider rather than something that's doing dithering or anything else more fancy. Given that it seems as well just to trust the clock rate we've got - we don't do any error tracking with the clock API (perhaps we should) and for many applications some degree of divergence from the nominal rate is not *too* bad for audio systems (for application specific values of some and too of course). If it is just dividers I'm not sure the situation is really improved materially by knocking off the top frequency. If we are doing something more fancy than divididing my analysis is off base of course. I'm thinking that the SSC samples the selected BCK pin using the (possibly divided) peripheral clock. Getting too near the theoretical rate limit would be bad, if these two independent clocks drift the wrong way. At least that is my take on it, but I don't know the internal workings of the SSC, so... I was hoping that someone from Atmel could chime in? Maybe I'm totally Sorry for late response. No problem! off base, and the SSC is doing this completely differently? What you mean here? I am not sure I fully understand. The SSC spec list a maximum rate (which varies with the direction of various signals, ignoring that for the sake of this explanation). Lets assume that this maximum rate is 11MHz, derived from the peripheral clock which might be 66MHz. If you then try to input an 11MHz signal derived from some unrelated xtal you might think it should work. My theory was that the rate limit would be broken if the peripheral clock wasn't really 66MHz, but instead a few ppm lower than nominal, and the unrelated xtal was a few ppm higher than nominal. If this matters or not depends on how the SSC is implemented. There might be other reasons for not caring all that much about this fringe case, and just trust the nominal rates and limits. In our application, we're not near the limit. Therefore, it really doesn't matter much to us. Should I resend w/o the 500ppm deduction? Cheers, Peter Best Regards, Bo Shen -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Bo Shen wrote: Hi Peter, On 02/09/2015 04:09 PM, Peter Rosin wrote: [Snip] /*-*\ * DAI functions @@ -200,6 +290,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct atmel_ssc_info *ssc_p = ssc_info[dai-id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; + int ret; pr_debug(atmel_ssc_startup: SSC_SR=0x%u\n, ssc_readl(ssc_p-ssc-regs, SR)); @@ -207,6 +298,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, /* Enable PMC peripheral clock for this SSC */ pr_debug(atmel_ssc_dai: Starting clock\n); clk_enable(ssc_p-ssc-clk); + ssc_p-mck_rate = clk_get_rate(ssc_p-ssc-clk) * 2; Why the mck_rate is calculated in this form? What did you have in mind? Add another clock to the ssc node in the device tree? IIUC, the device tree (at least normally) has the ssc clk as the peripheral clock divided by 2, but the ssc specifies (when capturing in the CBM/CFS case) the rate limit as the peripheral clock divided by 3 (i.e. ssc clk / 1.5). Since the SSC spec expresses the rate limit in terms of the peripheral clock, this was what I came up with. I didn't want to require dt changes... You make a misunderstand for the mck for ssc peripheral. The mck here is not the system mck, it only related with the ssc, it is the PMC output. For example, in device tree, the ssc clock divided by 2, then the pmc output for ssc is system mck / 2, so the ssc mck is system mck / 2. If divided by 4, then the ssc mck is system / 4 I think the reason for my misunderstanding might be that in the 3.10-at91 tree, the ssc clk is twice the rate compared to what it is in the 3.18-at91 tree. This made me assume that the ssc clk had been changed to mean the rate after the fixed divider by two that is activated as soon as the ssc clock divider (given by SSC_CMR) is activated, and that it was a simple matter of multiplying by two to get to the MCK rate. I further assumed that Master Clock in the Serial Clock Ratio Considerations section was this MCK. Maybe the mistake was to involve the peripheral clock at all? Ok, so I may have misunderstood, but in that case what does that mean in terms of finding the Master Clock rate that is mentioned in the Serial Clock Ratio Considerations section? Is it perhaps the rate of the parent clock of the given ssc clk? Or, given the above explanation, is it correct to simply multiply by two as I have done? [snip] Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH RESEND] pm: at91: Workaround DDRSDRC self-refresh bug with LPDDR1 memories
Nicolas Ferre wrote: Le 14/01/2015 14:20, Peter Rosin a écrit : From: Peter Rosin p...@axentia.se The DDRSDR controller (on the ATSAMA5D31) fails miserably to put LPDDR1 memories in self-refresh. Force the controller to think it has DDR2 memories during the self-refresh period, as the DDR2 self-refresh spec is equivalent to LPDDR1, and is correctly implemented in the controller. Assume that the second controller has the same fault, and that other CPUs in the family has the same problem, but that is untested. Signed-off-by: Peter Rosin p...@axentia.se I've just verified your code and the scope of this issue and your implementation makes perfect sense. Acked-by: Nicolas Ferre nicolas.fe...@atmel.com Peter, Thanks for your patch. You will probably see it appearing in 3.20 or 3.21. Great! Wenyou, Can you please integrate the patch from Peter in your current rework of the PM routines (keeping his authorship of course)? Please tell me if I can help with this. To be 100% honest with credits, I should perhaps have mentioned that I received an unconditional proof-of-concept patch from Atmel support (Case 7347). I'm not certain who wrote that patch, but the only thing that has survived is the idea to temporarily use DDR2 mode and the trivial change of the register field width in the header file. So, I do not feel too dishonest by claiming authorship... Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 2/2] ASoC: atmel_ssc_dai: remove clock pin comments
As the clock can be get from TK/RK pin, so remove the comments. Signed-off-by: Bo Shen voice.s...@atmel.com --- sound/soc/atmel/atmel_ssc_dai.c | 4 1 file changed, 4 deletions(-) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index e691aab..198661b 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -452,10 +452,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFM: /* * I2S format, CODEC supplies BCLK and LRC clocks. - * - * The SSC transmit clock is obtained from the BCLK signal on - * on the TK line, and the SSC receive clock is - * generated from the transmit clock. */ rcmr =SSC_BF(RCMR_PERIOD, 0) | SSC_BF(RCMR_STTDLY, START_DELAY) -- 2.3.0.rc0 Hi! You should probably remove that comment from the case with SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBM_CFM as well... Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 0/2] ASoC: pcm512x: Add knobs to allow and control overclocking
From: Peter Rosin p...@axentia.se Hi! I wasn't sure if I should add Documentation/* for these sysfs knobs or not? A lot of knobs do not seem to have docs (no specific example, just a gut feeling). And I'm not sure how I should name the doc-file since the pcm512x driver handles devices connected with both i2c and spi. And what KernelVersion should I enter? That depends... So, this isn't perfect, suggestions welcome. The first patch is a preparatory patch that makes 2/2 slightly simpler. I can fold them together if that's desired. Or I could split 2/2 up in three logical patches, one for each of the independent PLL/DSP/DAC knobs. Another feature that one might want, is that attempts to change the overclocking should fail with EBUSY when the device is already in use. But I haven't invested enough time to find out how I should determine when to fail, so for this version the driver accepts the change but doesn't enforce the new limit until the clock rates are recalculated. Cheers, Peter Peter Rosin (2): ASoC: pcm512x: Rearrange to not repeat dacsrc_rate / dac_div ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP Documentation/ABI/testing/sysfs-i2c-pcm512x | 35 + sound/soc/codecs/pcm512x.c | 189 --- 2 files changed, 205 insertions(+), 19 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-i2c-pcm512x -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 1/2] ASoC: pcm512x: Rearrange to not repeat dacsrc_rate / dac_div
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 19 ++- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 884784fb1566..f13ff7578c78 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -863,28 +863,29 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, dacsrc_rate = sck_rate; } + osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); + if (osr_div 128) { + dev_err(dev, Failed to find OSR divider\n); + return -EINVAL; + } + dac_div = DIV_ROUND_CLOSEST(dacsrc_rate, dac_rate); if (dac_div 128) { dev_err(dev, Failed to find DAC divider\n); return -EINVAL; } + dac_rate = dacsrc_rate / dac_div; - ncp_div = DIV_ROUND_CLOSEST(dacsrc_rate / dac_div, 1536000); - if (ncp_div 128 || dacsrc_rate / dac_div / ncp_div 2048000) { + ncp_div = DIV_ROUND_CLOSEST(dac_rate, 1536000); + if (ncp_div 128 || dac_rate / ncp_div 2048000) { /* run NCP no faster than 2048000 Hz, but why? */ - ncp_div = DIV_ROUND_UP(dacsrc_rate / dac_div, 2048000); + ncp_div = DIV_ROUND_UP(dac_rate, 2048000); if (ncp_div 128) { dev_err(dev, Failed to find NCP divider\n); return -EINVAL; } } - osr_div = DIV_ROUND_CLOSEST(dac_rate, osr_rate); - if (osr_div 128) { - dev_err(dev, Failed to find OSR divider\n); - return -EINVAL; - } - idac = mck_rate / (dsp_div * sample_rate); ret = regmap_write(pcm512x-regmap, PCM512x_DSP_CLKDIV, dsp_div - 1); -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 2/2] ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP
From: Peter Rosin p...@axentia.se When using non-standard rates, a relatively small amount of overclocking can make a big difference to a number of cases. - Not all rates are possible to achieve with the PLL, due to divider restrictions. - The higher oversampling rates that can be used by the DAC, the simpler the analog output filters get (mirror frequencies move up, away from the desired spectrum). - The more work the DSP can perform per sample, the better. For standard rates, there is little to gain as everything is designed just right, and the needed overclocking to make a real difference would be significant. Signed-off-by: Peter Rosin p...@axentia.se --- Documentation/ABI/testing/sysfs-i2c-pcm512x | 35 ++ sound/soc/codecs/pcm512x.c | 172 +-- 2 files changed, 196 insertions(+), 11 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-i2c-pcm512x diff --git a/Documentation/ABI/testing/sysfs-i2c-pcm512x b/Documentation/ABI/testing/sysfs-i2c-pcm512x new file mode 100644 index ..596cd97788db --- /dev/null +++ b/Documentation/ABI/testing/sysfs-i2c-pcm512x @@ -0,0 +1,35 @@ +What: /sys/bus/i2c/devices/busnum-devaddr/overclock_pll +Date: February 2015 +Contact: Peter Rosin p...@axentia.se +Description: When the codec acts as clock master, tell the pcm512x + to allow overclocking the PLL (as a percentage). Zero + disables PLL overclocking. + + Reading: returns the current PLL overclocking setting. + + Writing: set a new PLL overclocking setting. + Accepted values: 0..20. + +What: /sys/bus/i2c/devices/busnum-devaddr/overclock_dac +Date: February 2015 +Contact: Peter Rosin p...@axentia.se +Description: When the codec acts as clock master, tell the pcm512x + to allow overclocking the DAC (as a percentage). Zero + disables DAC overclocking. + + Reading: returns the current DAC overclocking setting. + + Writing: set a new DAC overclocking setting. + Accepted values: 0..40. + +What: /sys/bus/i2c/devices/busnum-devaddr/overclock_dsp +Date: February 2015 +Contact: Peter Rosin p...@axentia.se +Description: When the codec acts as clock master, tell the pcm512x + to allow overclocking the DSP (as a percentage). Zero + disables DSP overclocking. + + Reading: returns the current DSP overclocking setting. + + Writing: set a new DSP overclocking setting. + Accepted values: 0..40. diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index f13ff7578c78..f4d3dc390aed 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -54,6 +54,9 @@ struct pcm512x_priv { int pll_d; int pll_p; unsigned long real_pll; + unsigned long overclock_pll; + unsigned long overclock_dac; + unsigned long overclock_dsp; }; /* @@ -346,6 +349,132 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { OUTR, NULL, DACR }, }; +static ssize_t pcm512x_overclock_pll(struct device *dev, +struct device_attribute *attr, char *buf) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + + return sprintf(buf, %lu\n, pcm512x-overclock_pll); +} + +static ssize_t pcm512x_overclock_pll_set(struct device *dev, +struct device_attribute *attr, +const char *buf, size_t count) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + unsigned long value; + int ret; + + ret = kstrtoul(buf, 10, value); + if (ret) + return ret; + if (value 20) + return -EINVAL; + pcm512x-overclock_pll = value; + + return count; +} + +static DEVICE_ATTR(overclock_pll, 0644, + pcm512x_overclock_pll, pcm512x_overclock_pll_set); + +static ssize_t pcm512x_overclock_dsp(struct device *dev, +struct device_attribute *attr, char *buf) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + + return sprintf(buf, %lu\n, pcm512x-overclock_dsp); +} + +static ssize_t pcm512x_overclock_dsp_set(struct device *dev, +struct device_attribute *attr, +const char *buf, size_t count) +{ + struct pcm512x_priv *pcm512x = dev_get_drvdata(dev); + unsigned long value; + int ret; + + ret = kstrtoul(buf, 10, value); + if (ret) + return ret; + if (value 40) + return -EINVAL; + pcm512x-overclock_dsp = value; + + return count; +} + +static DEVICE_ATTR(overclock_dsp, 0644, + pcm512x_overclock_dsp
[PATCH 0/2] ASoC: pcm512x: Add knobs to allow and control overclocking
From: Peter Rosin p...@axentia.se Hi! I wasn't sure if I should add Documentation/* for these sysfs knobs or not? A lot of knobs do not have docs... And I'm not sure how I should name the doc-file since the pcm512x driver handles devices connected with both i2c and spi. So, this isn't perfect, suggestions welcome. The first patch is a preparatory patch that makes 2/2 slightly simpler. I can fold them together if that's desired. Or I could split 2/2 up in three logical pieces, one for each of the independent PLL/DSP/DAC knob. Whatever... Cheers, Peter Peter Rosin (2): ASoC: pcm512x: Rearrange to not repeat dacsrc_rate / dac_div ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP Documentation/ABI/testing/sysfs-i2c-pcm512x | 35 + sound/soc/codecs/pcm512x.c | 189 --- 2 files changed, 205 insertions(+), 19 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-i2c-pcm512x -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 1/7 RESEND] ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges()
From: Peter Rosin p...@axentia.se Add helper functions to allow drivers to specify several disjoint ranges for a variable. In particular, there is a codec (PCM512x) that has a hole in its supported range of rates, due to PLL and divider restrictions. This is like snd_pcm_hw_constraint_list(), but for ranges instead of points. Signed-off-by: Peter Rosin p...@axentia.se Reviewed-by: Lars-Peter Clausen l...@metafoo.de --- include/sound/pcm.h | 12 +++ sound/core/pcm_lib.c | 85 ++ 2 files changed, 97 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 1e7f74acc2ec..04fc037e0555 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -275,6 +275,12 @@ struct snd_pcm_hw_constraint_list { unsigned int mask; }; +struct snd_pcm_hw_constraint_ranges { + unsigned int count; + const struct snd_interval *ranges; + unsigned int mask; +}; + struct snd_pcm_hwptr_log; struct snd_pcm_runtime { @@ -910,6 +916,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, const struct snd_interval *b, struct snd_interval *c); int snd_interval_list(struct snd_interval *i, unsigned int count, const unsigned int *list, unsigned int mask); +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *list, unsigned int mask); int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp); @@ -934,6 +942,10 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, const struct snd_pcm_hw_constraint_list *l); +int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, +unsigned int cond, +snd_pcm_hw_param_t var, +const struct snd_pcm_hw_constraint_ranges *r); int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ec9e7866177f..446c00bd908b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, EXPORT_SYMBOL(snd_interval_list); +/** + * snd_interval_ranges - refine the interval value from the list of ranges + * @i: the interval value to refine + * @count: the number of elements in the list of ranges + * @ranges: the ranges list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list of ranges. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. + */ +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *ranges, unsigned int mask) +{ + unsigned int k; + struct snd_interval range_union; + struct snd_interval range; + + if (!count) { + snd_interval_none(i); + return -EINVAL; + } + snd_interval_any(range_union); + range_union.min = UINT_MAX; + range_union.max = 0; + for (k = 0; k count; k++) { + if (mask !(mask (1 k))) + continue; + snd_interval_copy(range, ranges[k]); + if (snd_interval_refine(range, i) 0) + continue; + if (snd_interval_empty(range)) + continue; + + if (range.min range_union.min) { + range_union.min = range.min; + range_union.openmin = 1; + } + if (range.min == range_union.min !range.openmin) + range_union.openmin = 0; + if (range.max range_union.max) { + range_union.max = range.max; + range_union.openmax = 1; + } + if (range.max == range_union.max !range.openmax) + range_union.openmax = 0; + } + return snd_interval_refine(i, range_union); +} +EXPORT_SYMBOL(snd_interval_ranges); + static int snd_interval_step(struct snd_interval *i, unsigned int step) { unsigned int n; @@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct
[PATCH 2/7 RESEND] ASoC: pcm512x: Fix spelling of register field names.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- sound/soc/codecs/pcm512x.h |6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index e5f2fb884bf3..874723c36d65 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -277,7 +277,7 @@ SOC_ENUM(Auto Mute Time Right, pcm512x_autom_r), SOC_SINGLE(Auto Mute Mono Switch, PCM512x_DIGITAL_MUTE_3, PCM512x_ACTL_SHIFT, 1, 0), SOC_DOUBLE(Auto Mute Switch, PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, - PCM512x_AMLR_SHIFT, 1, 0), + PCM512x_AMRE_SHIFT, 1, 0), SOC_ENUM(Volume Ramp Down Rate, pcm512x_vndf), SOC_ENUM(Volume Ramp Down Step, pcm512x_vnds), diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index 6ee76aaca09a..28b3dfd302bc 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -108,8 +108,8 @@ #define PCM512x_RQML_SHIFT 4 /* Page 0, Register 4 - PLL */ -#define PCM512x_PLCE (1 0) -#define PCM512x_RLCE_SHIFT 0 +#define PCM512x_PLLE (1 0) +#define PCM512x_PLLE_SHIFT 0 #define PCM512x_PLCK (1 4) #define PCM512x_PLCK_SHIFT 4 @@ -152,7 +152,7 @@ /* Page 0, Register 65 - Digital mute enables */ #define PCM512x_ACTL_SHIFT 2 #define PCM512x_AMLE_SHIFT 1 -#define PCM512x_AMLR_SHIFT 0 +#define PCM512x_AMRE_SHIFT 0 /* Page 1, Register 2 - analog volume control */ #define PCM512x_RAGN_SHIFT 0 -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 0/7 RESEND] ASoC: pcm512x: Clock master modes
From: Peter Rosin p...@axentia.se Hi! [ Note that the dt change is in patch 5/7, for those only interested in that particular bit. ] This series implements BCLK master modes for the pcm512x driver. It has only been tested with the pcm5142 chip, but they are from the same family and should be compatible. I have mainly used the spec for the newer pcm5242 chip (also from the same family) as it fills in a lot of blanks in the pcm512x/pcm514x specs. The code has also seen most of its testing in a 3.10 environment, so there might be some forward-porting warts. But it is able to play sound in 3.18 as well, and most of the changes have little to do with anything but the clocking in the chip itself. This is technically not a clean resend, since I'm now sending the whole series to all parties, hopefully using the correct email addesses this time. I have also augmented the commit message of patch 1/7 according to the review comments by Lars-Peter Clausen. Cheers, Peter Peter Rosin (7): ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges() ASoC: pcm512x: Fix spelling of register field names. ASoC: pcm512x: Change register default to match actual content after reset ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL ASoC: pcm512x: Support mastering BCLK/LRCLK using the PLL ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS .../devicetree/bindings/sound/pcm512x.txt | 25 +- include/sound/pcm.h| 12 + sound/core/pcm_lib.c | 85 ++ sound/soc/codecs/pcm512x.c | 955 +++- sound/soc/codecs/pcm512x.h | 109 ++- 5 files changed, 1161 insertions(+), 25 deletions(-) -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 4/7 RESEND] ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL
From: Peter Rosin p...@axentia.se Use register field names from the seemingly compatible PCM5242 datasheet, as the PCM512x and PCM514x datasheets are severly lacking. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 452 ++-- sound/soc/codecs/pcm512x.h | 57 +- 2 files changed, 492 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 4c65eb9ab59b..ac9a0b25b863 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -23,6 +23,7 @@ #include linux/regulator/consumer.h #include sound/soc.h #include sound/soc-dapm.h +#include sound/pcm_params.h #include sound/tlv.h #include pcm512x.h @@ -39,6 +40,7 @@ struct pcm512x_priv { struct clk *sclk; struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; + int fmt; }; /* @@ -69,6 +71,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_DAC_ROUTING, 0x11 }, { PCM512x_DSP_PROGRAM, 0x01 }, { PCM512x_CLKDET,0x00 }, @@ -87,6 +90,18 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, { PCM512x_VCOM_CTRL_1, 0x00 }, { PCM512x_VCOM_CTRL_2, 0x01 }, + { PCM512x_BCLK_LRCLK_CFG,0x00 }, + { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_DSP_CLKDIV,0x00 }, + { PCM512x_DAC_CLKDIV,0x00 }, + { PCM512x_NCP_CLKDIV,0x00 }, + { PCM512x_OSR_CLKDIV,0x00 }, + { PCM512x_MASTER_CLKDIV_1, 0x00 }, + { PCM512x_MASTER_CLKDIV_2, 0x00 }, + { PCM512x_FS_SPEED_MODE, 0x00 }, + { PCM512x_IDAC_1,0x01 }, + { PCM512x_IDAC_2,0x00 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) @@ -103,6 +118,8 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_DSP_GPIO_INPUT: case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: + case PCM512x_DAC_REF: + case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: case PCM512x_PLL_COEFF_2: @@ -303,6 +320,105 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { OUTR, NULL, DACR }, }; +static const u32 pcm512x_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_slave = { + .count = ARRAY_SIZE(pcm512x_dai_rates), + .list = pcm512x_dai_rates, +}; + +static int pcm512x_params_to_frame_size(struct snd_pcm_hw_params *params) +{ + int sample_size; + + sample_size = snd_pcm_format_physical_width(params_format(params)); + if (sample_size 0) + return sample_size; + + return snd_soc_calc_frame_size(sample_size, params_channels(params), 1); +} + +static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device *dev = dai-dev; + struct snd_pcm_hw_constraint_ratnums *constraints_no_pll; + struct snd_ratnum *rats_no_pll; + + if (IS_ERR(pcm512x-sclk)) { + dev_err(dev, Need SCLK for master mode: %ld\n, + PTR_ERR(pcm512x-sclk)); + return PTR_ERR(pcm512x-sclk); + } + + constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll), + GFP_KERNEL); + if (!constraints_no_pll) + return -ENOMEM; + constraints_no_pll-nrats = 1; + rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL); + if (!rats_no_pll) + return -ENOMEM; + constraints_no_pll-rats = rats_no_pll; + rats_no_pll-num = clk_get_rate(pcm512x-sclk) / 64; + rats_no_pll-den_min = 1; + rats_no_pll-den_max = 128; + rats_no_pll-den_step = 1; + + return snd_pcm_hw_constraint_ratnums(substream-runtime, 0, +SNDRV_PCM_HW_PARAM_RATE, +constraints_no_pll); +} + +static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, +struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device
[PATCH 5/7 RESEND] ASoC: pcm512x: Support mastering BCLK/LRCLK using the PLL
From: Peter Rosin p...@axentia.se Using the PLL in master mode requires using an external connection between one of the GPIO pins (configured as PLL/4 output) and the SCK pin. It also requires the external clock to be fed to some other GPIO pin instead of the SCK pin. This is described for the PCM5122 chip in the answers to the forum post PCM5122 DAC as I2S master troubles with PLL mode at the TI E2E community pages (1). The clocking functionality is also much better described in the datasheet for the chip PCM5242, which seems to be register compatible with PCM512x and PCM514x (which both have severely lacking datasheets). (1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/267830 Signed-off-by: Peter Rosin p...@axentia.se --- .../devicetree/bindings/sound/pcm512x.txt | 25 +- sound/soc/codecs/pcm512x.c | 469 +++- sound/soc/codecs/pcm512x.h | 44 +- 3 files changed, 512 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 98e0d34915e8..3aae3b41bd8e 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -17,9 +17,16 @@ Required properties: Optional properties: - clocks : A clock specifier for the clock connected as SCLK. If this -is absent the device will be configured to clock from BCLK. +is absent the device will be configured to clock from BCLK. If pll-in +and pll-out are specified in addition to a clock, the device is +configured to accept clock input on a specified gpio pin. -Example: + - pll-in, pll-out : gpio pins used to connect the pll using 1 +through 6. The device will be configured for clock input on the +given pll-in pin and PLL output on the given pll-out pin. An +external connection from the pll-out pin to the SCLK pin is assumed. + +Examples: pcm5122: pcm5122@4c { compatible = ti,pcm5122; @@ -29,3 +36,17 @@ Example: DVDD-supply = reg_1v8; CPVDD-supply = reg_3v3; }; + + + pcm5142: pcm5142@4c { + compatible = ti,pcm5142; + reg = 0x4c; + + AVDD-supply = reg_3v3_analog; + DVDD-supply = reg_1v8; + CPVDD-supply = reg_3v3; + + clocks = sck; + pll-in = 3; + pll-out = 6; + }; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index ac9a0b25b863..57940ea8b139 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -21,6 +21,7 @@ #include linux/pm_runtime.h #include linux/regmap.h #include linux/regulator/consumer.h +#include linux/gcd.h #include sound/soc.h #include sound/soc-dapm.h #include sound/pcm_params.h @@ -28,6 +29,11 @@ #include pcm512x.h +#define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) +#define DIV_ROUND_CLOSEST_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) + #define PCM512x_NUM_SUPPLIES 3 static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { AVDD, @@ -41,6 +47,13 @@ struct pcm512x_priv { struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; int fmt; + int pll_in; + int pll_out; + int pll_r; + int pll_j; + int pll_d; + int pll_p; + unsigned long real_pll; }; /* @@ -92,7 +105,13 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG,0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_PLLIN,0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_PLL_COEFF_0, 0x00 }, + { PCM512x_PLL_COEFF_1, 0x00 }, + { PCM512x_PLL_COEFF_2, 0x00 }, + { PCM512x_PLL_COEFF_3, 0x00 }, + { PCM512x_PLL_COEFF_4, 0x00 }, { PCM512x_DSP_CLKDIV,0x00 }, { PCM512x_DAC_CLKDIV,0x00 }, { PCM512x_NCP_CLKDIV,0x00 }, @@ -119,6 +138,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: case PCM512x_DAC_REF: + case PCM512x_GPIO_PLLIN: case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: @@ -160,6 +180,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_RATE_DET_2: case PCM512x_RATE_DET_3: case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: case PCM512x_ANALOG_MUTE_DET: case PCM512x_GPIN: case PCM512x_DIGITAL_MUTE_DET: @@ -171,6 +192,8 @@ static bool pcm512x_readable
[PATCH 7/7 RESEND] ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 13 +++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 7f45cc468fa1..33aa18c8c88e 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -486,6 +486,7 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream, switch (pcm512x-fmt SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: return pcm512x_dai_startup_master(substream, dai); case SND_SOC_DAIFMT_CBS_CFS: @@ -992,6 +993,8 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); int alen; int gpio; + int clock_output; + int master_mode; int ret; dev_dbg(codec-dev, hw_params %u Hz, %u channels\n, @@ -1040,6 +1043,12 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, } return 0; case SND_SOC_DAIFMT_CBM_CFM: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + master_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBM_CFS: + clock_output = PCM512x_BCKO; + master_mode = PCM512x_RBCK; break; default: return -EINVAL; @@ -1136,7 +1145,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(pcm512x-regmap, PCM512x_BCLK_LRCLK_CFG, PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, -PCM512x_BCKO | PCM512x_LRKO); +clock_output); if (ret != 0) { dev_err(codec-dev, Failed to enable clock output: %d\n, ret); return ret; @@ -1144,7 +1153,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(pcm512x-regmap, PCM512x_MASTER_MODE, PCM512x_RLRK | PCM512x_RBCK, -PCM512x_RLRK | PCM512x_RBCK); +master_mode); if (ret != 0) { dev_err(codec-dev, Failed to enable master mode: %d\n, ret); return ret; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 6/7 RESEND] ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible
From: Peter Rosin p...@axentia.se The PLL introduces jitter, which in turn introduces noice if used to clock the DAC. Thus, avoid the PLL output, and use the PLL input to drive the DAC clock, if possible. This is described for the PCM5142/PCM5242 chips in the answers to the forum post PCM5142/PCM5242 DAC clock source at the TI E2E community pages (1). (1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/389994 Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 119 ++-- sound/soc/codecs/pcm512x.h |4 +- 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 57940ea8b139..7f45cc468fa1 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -105,6 +105,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG,0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_DACIN,0x00 }, { PCM512x_GPIO_PLLIN,0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, { PCM512x_PLL_COEFF_0, 0x00 }, @@ -138,6 +139,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: case PCM512x_DAC_REF: + case PCM512x_GPIO_DACIN: case PCM512x_GPIO_PLLIN: case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: @@ -681,6 +683,37 @@ done: return 0; } +static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, + unsigned long osr_rate, + unsigned long pllin_rate) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + unsigned long dac_rate; + + if (!pcm512x-pll_out) + return 0; /* no PLL to bypass, force SCK as DAC input */ + + if (pllin_rate % osr_rate) + return 0; /* futile, quit early */ + + /* run DAC no faster than 6144000 Hz */ + for (dac_rate = rounddown(6144000, osr_rate); +dac_rate; +dac_rate -= osr_rate) { + + if (pllin_rate / dac_rate 128) + return 0; /* DAC divider would be too big */ + + if (!(pllin_rate % dac_rate)) + return dac_rate; + + dac_rate -= osr_rate; + } + + return 0; +} + static int pcm512x_set_dividers(struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { @@ -694,6 +727,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, unsigned long bclk_rate; unsigned long sample_rate; unsigned long osr_rate; + unsigned long dacsrc_rate; int bclk_div; int lrclk_div; int dsp_div; @@ -701,11 +735,10 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, unsigned long dac_rate; int ncp_div; int osr_div; - unsigned long dac_mul; - unsigned long sck_mul; int ret; int idac; int fssp; + int gpio; lrclk_div = pcm512x_params_to_frame_size(params); if (lrclk_div == 0) { @@ -794,31 +827,72 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, /* run DSP no faster than 50 MHz */ dsp_div = mck_rate 5000 ? 2 : 1; - /* run DAC no faster than 6144000 Hz */ - dac_mul = 6144000 / osr_rate; - sck_mul = sck_rate / osr_rate; - for (; dac_mul; dac_mul--) { - if (!(sck_mul % dac_mul)) - break; - } - if (!dac_mul) { - dev_err(dev, Failed to find DAC rate\n); - return -EINVAL; - } + dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); + if (dac_rate) { + /* the desired clock rate is compatible with the pll input +* clock, so use that clock as dac input instead of the pll +* output clock since the pll will introduce jitter and thus +* noise. +*/ + dev_dbg(dev, using pll input as dac input\n); + ret = regmap_update_bits(pcm512x-regmap, PCM512x_DAC_REF, +PCM512x_SDAC, PCM512x_SDAC_GPIO); + if (ret != 0) { + dev_err(codec-dev, + Failed to set gpio as dacref: %d\n, ret); + return ret; + } - dac_rate = dac_mul * osr_rate; - dev_dbg(dev, dac_rate %lu sample_rate %lu\n, dac_rate, sample_rate); + gpio = PCM512x_GREF_GPIO1 + pcm512x-pll_in - 1; + ret = regmap_update_bits(pcm512x-regmap, PCM512x_GPIO_DACIN
[PATCH 3/7 RESEND] ASoC: pcm512x: Change register default to match actual content after reset
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 874723c36d65..4c65eb9ab59b 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -78,7 +78,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_DIGITAL_VOLUME_2, 0x30 }, { PCM512x_DIGITAL_VOLUME_3, 0x30 }, { PCM512x_DIGITAL_MUTE_1,0x22 }, - { PCM512x_DIGITAL_MUTE_2,0x00 }, + { PCM512x_DIGITAL_MUTE_2,0x02 }, { PCM512x_DIGITAL_MUTE_3,0x07 }, { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v3 03/13] pm: at91: Workaround DDRSDRC self-refresh bug with LPDDR1 memories.
I wrote: Sergei Shtylyov wrote: On 1/27/2015 8:53 AM, Wenyou Yang wrote: From: Peter Rosin p...@axentia.se The DDRSDR controller fails miserably to put LPDDR1 memories in self-refresh. Force the controller to think it has DDR2 memories during the self-refresh period, as the DDR2 self-refresh spec is equivalent to LPDDR1, and is correctly implemented in the controller. Assume that the second controller has the same fault, but that is untested. Signed-off-by: Peter Rosin p...@axentia.se Acked-by: Nicolas Ferre nicolas.fe...@atmel.com --- arch/arm/mach-at91/pm_slowclock.S | 43 +++- include/soc/at91/at91sam9_ddrsdr.h |2 +- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index e2bfaf5..1155217 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S [...] @@ -108,14 +118,26 @@ ddr_sr_enable: /* figure out if we use the second ram controller */ cmp ramc1, #0 - ldrne tmp2, [ramc1, #AT91_DDRSDRC_LPR] - strne tmp2, .saved_sam9_lpr1 - bicne tmp2, #AT91_DDRSDRC_LPCB - orrne tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH + beq ddr_no_2nd_ctrl + + ldr tmp2, [ramc1, #AT91_DDRSDRC_MDR] + str tmp2, .saved_sam9_mdr1 + bic tmp2, tmp2, #~AT91_DDRSDRC_MD + cmp tmp2, #AT91_DDRSDRC_MD_LOW_POWER_DDR + ldreq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + biceq tmp2, tmp2, #AT91_DDRSDRC_MD Didn't you forget ~? Either that, or ~ above is not needed, I think. The code is correct, the first bic with ~ clears bits not in the relevant field in order to compare if LPDDR mode is active. The second bic(eq) w/o ~ clears the field, to make way for the bits in the below orreq when actually changing the register content into DDR2 mode. + orreq tmp2, tmp2, #AT91_DDRSDRC_MD_DDR2 + streq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + + ldr tmp2, [ramc1, #AT91_DDRSDRC_LPR] + str tmp2, .saved_sam9_lpr1 + bic tmp2, #AT91_DDRSDRC_LPCB Didn't you forget ~? And isn't it 3-operand instruction (as seen in the above code)? The logic for the LPR register is from the old code, the only thing I did to it was changing the instruction sequence to not have the ???ne form, i.e. ldrne, strne, bicne, orrne became ldr, str, bic, orr with a jump around it instead. So, the original code also had a two argument bic(ne), which indeed is strange, and I don't know why there is no warning from the assembler. Since there is no warning, my guess is that the assembler somehow mends it? Or does the patch actually break the second controller? It would be a surprise it the assembler handles 2-operand bicne differently from a s/it the/if the/ 2-operand bic, no? + orr tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH Only 2 operands? Same argument as above. I didn't touch it (sort of...) Should I update the patch and fix this collateral 2-operand problem as well? To me, it feels like a separate patch, no? I have now checked the assembler output, and apparently it mends the input, just as I thought. That might be a fluke, of course, or it might be a deliberate shorthand when the destination register is the same as the following operand? But I also note that there are more instances of this 2 vs. 3 argument syntax, and I suggest that they are all fixed in one go, if it is determined that they need fixing. I am obviously not an authority when it comes to arm assembler syntax, so someone else will have to advise... Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v3 03/13] pm: at91: Workaround DDRSDRC self-refresh bug with LPDDR1 memories.
Sergei Shtylyov wrote: Hello. Hi! On 1/27/2015 8:53 AM, Wenyou Yang wrote: From: Peter Rosin p...@axentia.se The DDRSDR controller fails miserably to put LPDDR1 memories in self-refresh. Force the controller to think it has DDR2 memories during the self-refresh period, as the DDR2 self-refresh spec is equivalent to LPDDR1, and is correctly implemented in the controller. Assume that the second controller has the same fault, but that is untested. Signed-off-by: Peter Rosin p...@axentia.se Acked-by: Nicolas Ferre nicolas.fe...@atmel.com --- arch/arm/mach-at91/pm_slowclock.S | 43 +++- include/soc/at91/at91sam9_ddrsdr.h |2 +- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index e2bfaf5..1155217 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S [...] @@ -108,14 +118,26 @@ ddr_sr_enable: /* figure out if we use the second ram controller */ cmp ramc1, #0 - ldrne tmp2, [ramc1, #AT91_DDRSDRC_LPR] - strne tmp2, .saved_sam9_lpr1 - bicne tmp2, #AT91_DDRSDRC_LPCB - orrne tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH + beq ddr_no_2nd_ctrl + + ldr tmp2, [ramc1, #AT91_DDRSDRC_MDR] + str tmp2, .saved_sam9_mdr1 + bic tmp2, tmp2, #~AT91_DDRSDRC_MD + cmp tmp2, #AT91_DDRSDRC_MD_LOW_POWER_DDR + ldreq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + biceq tmp2, tmp2, #AT91_DDRSDRC_MD Didn't you forget ~? Either that, or ~ above is not needed, I think. The code is correct, the first bic with ~ clears bits not in the relevant field in order to compare if LPDDR mode is active. The second bic(eq) w/o ~ clears the field, to make way for the bits in the below orreq when actually changing the register content into DDR2 mode. + orreq tmp2, tmp2, #AT91_DDRSDRC_MD_DDR2 + streq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + + ldr tmp2, [ramc1, #AT91_DDRSDRC_LPR] + str tmp2, .saved_sam9_lpr1 + bic tmp2, #AT91_DDRSDRC_LPCB Didn't you forget ~? And isn't it 3-operand instruction (as seen in the above code)? The logic for the LPR register is from the old code, the only thing I did to it was changing the instruction sequence to not have the ???ne form, i.e. ldrne, strne, bicne, orrne became ldr, str, bic, orr with a jump around it instead. So, the original code also had a two argument bic(ne), which indeed is strange, and I don't know why there is no warning from the assembler. Since there is no warning, my guess is that the assembler somehow mends it? Or does the patch actually break the second controller? It would be a surprise it the assembler handles 2-operand bicne differently from a 2-operand bic, no? + orr tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH Only 2 operands? Same argument as above. I didn't touch it (sort of...) Should I update the patch and fix this collateral 2-operand problem as well? To me, it feels like a separate patch, no? [...] WBR, Sergei Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v2 3/7] ASoC: pcm512x: Change register default to match actual content after reset
Hi Mark, First of all, thanks for taking the rest of the series! Mark Brown wrote: On Wed, Jan 28, 2015 at 03:16:08PM +0100, Peter Rosin wrote: @@ -78,7 +78,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_DIGITAL_VOLUME_2, 0x30 }, { PCM512x_DIGITAL_VOLUME_3, 0x30 }, { PCM512x_DIGITAL_MUTE_1,0x22 }, - { PCM512x_DIGITAL_MUTE_2,0x00 }, + { PCM512x_DIGITAL_MUTE_2,0x02 }, The datasheet claims these have undefined values as reserved bits - what would be a more robust change here would be to remove the register default entirely so that we take the value the hardware has, giving robustness against any hardware revisions. Of course the device has rather a lot of such reserved bits which is unfortunate. Given the incompleteness of the datasheets this might be safest; a lot of the reserved areas appear to have undocumented functions. But it works as is of course, so it is not high priority... I don't know what happens when this particular bit is cleared, but it doesn't change anything for the pcm5142 that I am able to detect. So, no real problem with dropping 3/7. Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2 0/7] ASoC: pcm512x: Clock master modes
From: Peter Rosin p...@axentia.se Hi! [ Note that the dt change is in patch 5/7, for those only interested in that particular bit. ] This series implements BCLK master modes for the pcm512x driver. It has only been tested with the pcm5142 chip, but they are from the same family and should be compatible. I have mainly used the spec for the newer pcm5242 chip (also from the same family) as it fills in a lot of blanks in the pcm512x/pcm514x specs. The code has also seen most of its testing in a 3.10 environment, so there might be some forward-porting warts. But it is able to play sound in 3.18 as well, and most of the changes have little to do with anything but the clocking in the chip itself. Changes since v1: - Use snd_soc_params_to_frame_size and snd_soc_params_to_bclk instead of implementing home-grown versions based on the physical width. The I2S-communication will no longer have padding bits for S24_LE. Cheers, Peter Peter Rosin (7): ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges() ASoC: pcm512x: Fix spelling of register field names. ASoC: pcm512x: Change register default to match actual content after reset ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL ASoC: pcm512x: Support mastering BCLK/LRCLK using the PLL ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS .../devicetree/bindings/sound/pcm512x.txt | 25 +- include/sound/pcm.h| 12 + sound/core/pcm_lib.c | 85 ++ sound/soc/codecs/pcm512x.c | 933 +++- sound/soc/codecs/pcm512x.h | 109 ++- 5 files changed, 1139 insertions(+), 25 deletions(-) -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2 1/7] ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges()
From: Peter Rosin p...@axentia.se Add helper functions to allow drivers to specify several disjoint ranges for a variable. In particular, there is a codec (PCM512x) that has a hole in its supported range of rates, due to PLL and divider restrictions. This is like snd_pcm_hw_constraint_list(), but for ranges instead of points. Signed-off-by: Peter Rosin p...@axentia.se Reviewed-by: Lars-Peter Clausen l...@metafoo.de --- include/sound/pcm.h | 12 +++ sound/core/pcm_lib.c | 85 ++ 2 files changed, 97 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 1e7f74acc2ec..04fc037e0555 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -275,6 +275,12 @@ struct snd_pcm_hw_constraint_list { unsigned int mask; }; +struct snd_pcm_hw_constraint_ranges { + unsigned int count; + const struct snd_interval *ranges; + unsigned int mask; +}; + struct snd_pcm_hwptr_log; struct snd_pcm_runtime { @@ -910,6 +916,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, const struct snd_interval *b, struct snd_interval *c); int snd_interval_list(struct snd_interval *i, unsigned int count, const unsigned int *list, unsigned int mask); +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *list, unsigned int mask); int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp); @@ -934,6 +942,10 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, const struct snd_pcm_hw_constraint_list *l); +int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, +unsigned int cond, +snd_pcm_hw_param_t var, +const struct snd_pcm_hw_constraint_ranges *r); int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ec9e7866177f..446c00bd908b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, EXPORT_SYMBOL(snd_interval_list); +/** + * snd_interval_ranges - refine the interval value from the list of ranges + * @i: the interval value to refine + * @count: the number of elements in the list of ranges + * @ranges: the ranges list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list of ranges. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. + */ +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *ranges, unsigned int mask) +{ + unsigned int k; + struct snd_interval range_union; + struct snd_interval range; + + if (!count) { + snd_interval_none(i); + return -EINVAL; + } + snd_interval_any(range_union); + range_union.min = UINT_MAX; + range_union.max = 0; + for (k = 0; k count; k++) { + if (mask !(mask (1 k))) + continue; + snd_interval_copy(range, ranges[k]); + if (snd_interval_refine(range, i) 0) + continue; + if (snd_interval_empty(range)) + continue; + + if (range.min range_union.min) { + range_union.min = range.min; + range_union.openmin = 1; + } + if (range.min == range_union.min !range.openmin) + range_union.openmin = 0; + if (range.max range_union.max) { + range_union.max = range.max; + range_union.openmax = 1; + } + if (range.max == range_union.max !range.openmax) + range_union.openmax = 0; + } + return snd_interval_refine(i, range_union); +} +EXPORT_SYMBOL(snd_interval_ranges); + static int snd_interval_step(struct snd_interval *i, unsigned int step) { unsigned int n; @@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct
[PATCH v2 6/7] ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible
From: Peter Rosin p...@axentia.se The PLL introduces jitter, which in turn introduces noice if used to clock the DAC. Thus, avoid the PLL output, and use the PLL input to drive the DAC clock, if possible. This is described for the PCM5142/PCM5242 chips in the answers to the forum post PCM5142/PCM5242 DAC clock source at the TI E2E community pages (1). (1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/389994 Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 119 ++-- sound/soc/codecs/pcm512x.h |4 +- 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index f0c7ec2a2f31..d46d6cdb6b87 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -105,6 +105,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG,0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_DACIN,0x00 }, { PCM512x_GPIO_PLLIN,0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, { PCM512x_PLL_COEFF_0, 0x00 }, @@ -138,6 +139,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: case PCM512x_DAC_REF: + case PCM512x_GPIO_DACIN: case PCM512x_GPIO_PLLIN: case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: @@ -659,6 +661,37 @@ done: return 0; } +static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, + unsigned long osr_rate, + unsigned long pllin_rate) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + unsigned long dac_rate; + + if (!pcm512x-pll_out) + return 0; /* no PLL to bypass, force SCK as DAC input */ + + if (pllin_rate % osr_rate) + return 0; /* futile, quit early */ + + /* run DAC no faster than 6144000 Hz */ + for (dac_rate = rounddown(6144000, osr_rate); +dac_rate; +dac_rate -= osr_rate) { + + if (pllin_rate / dac_rate 128) + return 0; /* DAC divider would be too big */ + + if (!(pllin_rate % dac_rate)) + return dac_rate; + + dac_rate -= osr_rate; + } + + return 0; +} + static int pcm512x_set_dividers(struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { @@ -672,6 +705,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, unsigned long bclk_rate; unsigned long sample_rate; unsigned long osr_rate; + unsigned long dacsrc_rate; int bclk_div; int lrclk_div; int dsp_div; @@ -679,11 +713,10 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, unsigned long dac_rate; int ncp_div; int osr_div; - unsigned long dac_mul; - unsigned long sck_mul; int ret; int idac; int fssp; + int gpio; lrclk_div = snd_soc_params_to_frame_size(params); if (lrclk_div == 0) { @@ -772,31 +805,72 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, /* run DSP no faster than 50 MHz */ dsp_div = mck_rate 5000 ? 2 : 1; - /* run DAC no faster than 6144000 Hz */ - dac_mul = 6144000 / osr_rate; - sck_mul = sck_rate / osr_rate; - for (; dac_mul; dac_mul--) { - if (!(sck_mul % dac_mul)) - break; - } - if (!dac_mul) { - dev_err(dev, Failed to find DAC rate\n); - return -EINVAL; - } + dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); + if (dac_rate) { + /* the desired clock rate is compatible with the pll input +* clock, so use that clock as dac input instead of the pll +* output clock since the pll will introduce jitter and thus +* noise. +*/ + dev_dbg(dev, using pll input as dac input\n); + ret = regmap_update_bits(pcm512x-regmap, PCM512x_DAC_REF, +PCM512x_SDAC, PCM512x_SDAC_GPIO); + if (ret != 0) { + dev_err(codec-dev, + Failed to set gpio as dacref: %d\n, ret); + return ret; + } - dac_rate = dac_mul * osr_rate; - dev_dbg(dev, dac_rate %lu sample_rate %lu\n, dac_rate, sample_rate); + gpio = PCM512x_GREF_GPIO1 + pcm512x-pll_in - 1; + ret = regmap_update_bits(pcm512x-regmap, PCM512x_GPIO_DACIN
[PATCH v2 3/7] ASoC: pcm512x: Change register default to match actual content after reset
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 874723c36d65..4c65eb9ab59b 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -78,7 +78,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_DIGITAL_VOLUME_2, 0x30 }, { PCM512x_DIGITAL_VOLUME_3, 0x30 }, { PCM512x_DIGITAL_MUTE_1,0x22 }, - { PCM512x_DIGITAL_MUTE_2,0x00 }, + { PCM512x_DIGITAL_MUTE_2,0x02 }, { PCM512x_DIGITAL_MUTE_3,0x07 }, { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2 5/7] ASoC: pcm512x: Support mastering BCLK/LRCLK using the PLL
From: Peter Rosin p...@axentia.se Using the PLL in master mode requires using an external connection between one of the GPIO pins (configured as PLL/4 output) and the SCK pin. It also requires the external clock to be fed to some other GPIO pin instead of the SCK pin. This is described for the PCM5122 chip in the answers to the forum post PCM5122 DAC as I2S master troubles with PLL mode at the TI E2E community pages (1). The clocking functionality is also much better described in the datasheet for the chip PCM5242, which seems to be register compatible with PCM512x and PCM514x (which both have severely lacking datasheets). (1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/267830 Signed-off-by: Peter Rosin p...@axentia.se --- .../devicetree/bindings/sound/pcm512x.txt | 25 +- sound/soc/codecs/pcm512x.c | 458 +++- sound/soc/codecs/pcm512x.h | 44 +- 3 files changed, 501 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 98e0d34915e8..3aae3b41bd8e 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -17,9 +17,16 @@ Required properties: Optional properties: - clocks : A clock specifier for the clock connected as SCLK. If this -is absent the device will be configured to clock from BCLK. +is absent the device will be configured to clock from BCLK. If pll-in +and pll-out are specified in addition to a clock, the device is +configured to accept clock input on a specified gpio pin. -Example: + - pll-in, pll-out : gpio pins used to connect the pll using 1 +through 6. The device will be configured for clock input on the +given pll-in pin and PLL output on the given pll-out pin. An +external connection from the pll-out pin to the SCLK pin is assumed. + +Examples: pcm5122: pcm5122@4c { compatible = ti,pcm5122; @@ -29,3 +36,17 @@ Example: DVDD-supply = reg_1v8; CPVDD-supply = reg_3v3; }; + + + pcm5142: pcm5142@4c { + compatible = ti,pcm5142; + reg = 0x4c; + + AVDD-supply = reg_3v3_analog; + DVDD-supply = reg_1v8; + CPVDD-supply = reg_3v3; + + clocks = sck; + pll-in = 3; + pll-out = 6; + }; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 124388809d48..f0c7ec2a2f31 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -21,6 +21,7 @@ #include linux/pm_runtime.h #include linux/regmap.h #include linux/regulator/consumer.h +#include linux/gcd.h #include sound/soc.h #include sound/soc-dapm.h #include sound/pcm_params.h @@ -28,6 +29,11 @@ #include pcm512x.h +#define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) +#define DIV_ROUND_CLOSEST_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) + #define PCM512x_NUM_SUPPLIES 3 static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { AVDD, @@ -41,6 +47,13 @@ struct pcm512x_priv { struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; int fmt; + int pll_in; + int pll_out; + int pll_r; + int pll_j; + int pll_d; + int pll_p; + unsigned long real_pll; }; /* @@ -92,7 +105,13 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG,0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_PLLIN,0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_PLL_COEFF_0, 0x00 }, + { PCM512x_PLL_COEFF_1, 0x00 }, + { PCM512x_PLL_COEFF_2, 0x00 }, + { PCM512x_PLL_COEFF_3, 0x00 }, + { PCM512x_PLL_COEFF_4, 0x00 }, { PCM512x_DSP_CLKDIV,0x00 }, { PCM512x_DAC_CLKDIV,0x00 }, { PCM512x_NCP_CLKDIV,0x00 }, @@ -119,6 +138,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: case PCM512x_DAC_REF: + case PCM512x_GPIO_PLLIN: case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: @@ -160,6 +180,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_RATE_DET_2: case PCM512x_RATE_DET_3: case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: case PCM512x_ANALOG_MUTE_DET: case PCM512x_GPIN: case PCM512x_DIGITAL_MUTE_DET: @@ -171,6 +192,8 @@ static bool pcm512x_readable
[PATCH v2 7/7] ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 13 +++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index d46d6cdb6b87..64be85fb2748 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -464,6 +464,7 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream, switch (pcm512x-fmt SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: return pcm512x_dai_startup_master(substream, dai); case SND_SOC_DAIFMT_CBS_CFS: @@ -970,6 +971,8 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); int alen; int gpio; + int clock_output; + int master_mode; int ret; dev_dbg(codec-dev, hw_params %u Hz, %u channels\n, @@ -1018,6 +1021,12 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, } return 0; case SND_SOC_DAIFMT_CBM_CFM: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + master_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBM_CFS: + clock_output = PCM512x_BCKO; + master_mode = PCM512x_RBCK; break; default: return -EINVAL; @@ -1114,7 +1123,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(pcm512x-regmap, PCM512x_BCLK_LRCLK_CFG, PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, -PCM512x_BCKO | PCM512x_LRKO); +clock_output); if (ret != 0) { dev_err(codec-dev, Failed to enable clock output: %d\n, ret); return ret; @@ -1122,7 +1131,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(pcm512x-regmap, PCM512x_MASTER_MODE, PCM512x_RLRK | PCM512x_RBCK, -PCM512x_RLRK | PCM512x_RBCK); +master_mode); if (ret != 0) { dev_err(codec-dev, Failed to enable master mode: %d\n, ret); return ret; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2 2/7] ASoC: pcm512x: Fix spelling of register field names.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- sound/soc/codecs/pcm512x.h |6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index e5f2fb884bf3..874723c36d65 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -277,7 +277,7 @@ SOC_ENUM(Auto Mute Time Right, pcm512x_autom_r), SOC_SINGLE(Auto Mute Mono Switch, PCM512x_DIGITAL_MUTE_3, PCM512x_ACTL_SHIFT, 1, 0), SOC_DOUBLE(Auto Mute Switch, PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, - PCM512x_AMLR_SHIFT, 1, 0), + PCM512x_AMRE_SHIFT, 1, 0), SOC_ENUM(Volume Ramp Down Rate, pcm512x_vndf), SOC_ENUM(Volume Ramp Down Step, pcm512x_vnds), diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index 6ee76aaca09a..28b3dfd302bc 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -108,8 +108,8 @@ #define PCM512x_RQML_SHIFT 4 /* Page 0, Register 4 - PLL */ -#define PCM512x_PLCE (1 0) -#define PCM512x_RLCE_SHIFT 0 +#define PCM512x_PLLE (1 0) +#define PCM512x_PLLE_SHIFT 0 #define PCM512x_PLCK (1 4) #define PCM512x_PLCK_SHIFT 4 @@ -152,7 +152,7 @@ /* Page 0, Register 65 - Digital mute enables */ #define PCM512x_ACTL_SHIFT 2 #define PCM512x_AMLE_SHIFT 1 -#define PCM512x_AMLR_SHIFT 0 +#define PCM512x_AMRE_SHIFT 0 /* Page 1, Register 2 - analog volume control */ #define PCM512x_RAGN_SHIFT 0 -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2 4/7] ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL
From: Peter Rosin p...@axentia.se Use register field names from the seemingly compatible PCM5242 datasheet, as the PCM512x and PCM514x datasheets are severly lacking. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 441 ++-- sound/soc/codecs/pcm512x.h | 57 +- 2 files changed, 481 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 4c65eb9ab59b..124388809d48 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -23,6 +23,7 @@ #include linux/regulator/consumer.h #include sound/soc.h #include sound/soc-dapm.h +#include sound/pcm_params.h #include sound/tlv.h #include pcm512x.h @@ -39,6 +40,7 @@ struct pcm512x_priv { struct clk *sclk; struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; + int fmt; }; /* @@ -69,6 +71,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_DAC_ROUTING, 0x11 }, { PCM512x_DSP_PROGRAM, 0x01 }, { PCM512x_CLKDET,0x00 }, @@ -87,6 +90,18 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, { PCM512x_VCOM_CTRL_1, 0x00 }, { PCM512x_VCOM_CTRL_2, 0x01 }, + { PCM512x_BCLK_LRCLK_CFG,0x00 }, + { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_DSP_CLKDIV,0x00 }, + { PCM512x_DAC_CLKDIV,0x00 }, + { PCM512x_NCP_CLKDIV,0x00 }, + { PCM512x_OSR_CLKDIV,0x00 }, + { PCM512x_MASTER_CLKDIV_1, 0x00 }, + { PCM512x_MASTER_CLKDIV_2, 0x00 }, + { PCM512x_FS_SPEED_MODE, 0x00 }, + { PCM512x_IDAC_1,0x01 }, + { PCM512x_IDAC_2,0x00 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) @@ -103,6 +118,8 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_DSP_GPIO_INPUT: case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: + case PCM512x_DAC_REF: + case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: case PCM512x_PLL_COEFF_2: @@ -303,6 +320,94 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { OUTR, NULL, DACR }, }; +static const u32 pcm512x_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_slave = { + .count = ARRAY_SIZE(pcm512x_dai_rates), + .list = pcm512x_dai_rates, +}; + +static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device *dev = dai-dev; + struct snd_pcm_hw_constraint_ratnums *constraints_no_pll; + struct snd_ratnum *rats_no_pll; + + if (IS_ERR(pcm512x-sclk)) { + dev_err(dev, Need SCLK for master mode: %ld\n, + PTR_ERR(pcm512x-sclk)); + return PTR_ERR(pcm512x-sclk); + } + + constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll), + GFP_KERNEL); + if (!constraints_no_pll) + return -ENOMEM; + constraints_no_pll-nrats = 1; + rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL); + if (!rats_no_pll) + return -ENOMEM; + constraints_no_pll-rats = rats_no_pll; + rats_no_pll-num = clk_get_rate(pcm512x-sclk) / 64; + rats_no_pll-den_min = 1; + rats_no_pll-den_max = 128; + rats_no_pll-den_step = 1; + + return snd_pcm_hw_constraint_ratnums(substream-runtime, 0, +SNDRV_PCM_HW_PARAM_RATE, +constraints_no_pll); +} + +static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, +struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device *dev = dai-dev; + struct regmap *regmap = pcm512x-regmap; + + if (IS_ERR(pcm512x-sclk)) { + dev_info(dev, No SCLK, using BCLK: %ld\n, +PTR_ERR(pcm512x-sclk)); + + /* Disable reporting of missing SCLK as an error */ + regmap_update_bits(regmap
[PATCH] ASoC: atmel_ssc_dai: Support SND_SOC_DAIFMT_CBM_CFS on I2S
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 48 +++ 1 file changed, 48 insertions(+) diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 3cd70597d109..f55f3aab8bdd 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -485,6 +485,54 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream, | SSC_BF(TFMR_DATLEN, (bits - 1)); break; + case SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBM_CFS: + /* I2S format, CODEC supplies BCLK, SSC supplies LRCLK. */ + if (bits 16 !ssc-pdata-has_fslen_ext) { + dev_err(dai-dev, + sample size %d is too large for SSC device\n, + bits); + return -EINVAL; + } + + fslen_ext = (bits - 1) / 16; + fslen = (bits - 1) % 16; + + rcmr =SSC_BF(RCMR_PERIOD, ssc_p-rcmr_period) + | SSC_BF(RCMR_STTDLY, START_DELAY) + | SSC_BF(RCMR_START, SSC_START_FALLING_RF) + | SSC_BF(RCMR_CKI, SSC_CKI_RISING) + | SSC_BF(RCMR_CKO, SSC_CKO_NONE) + | SSC_BF(RCMR_CKS, ssc-clk_from_rk_pin ? + SSC_CKS_PIN : SSC_CKS_CLOCK); + + rfmr =SSC_BF(RFMR_FSLEN_EXT, fslen_ext) + | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE) + | SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE) + | SSC_BF(RFMR_FSLEN, fslen) + | SSC_BF(RFMR_DATNB, (channels - 1)) + | SSC_BIT(RFMR_MSBF) + | SSC_BF(RFMR_LOOP, 0) + | SSC_BF(RFMR_DATLEN, (bits - 1)); + + tcmr =SSC_BF(TCMR_PERIOD, ssc_p-tcmr_period) + | SSC_BF(TCMR_STTDLY, START_DELAY) + | SSC_BF(TCMR_START, SSC_START_FALLING_RF) + | SSC_BF(TCMR_CKI, SSC_CKI_FALLING) + | SSC_BF(TCMR_CKO, SSC_CKO_NONE) + | SSC_BF(TCMR_CKS, ssc-clk_from_rk_pin ? + SSC_CKS_CLOCK : SSC_CKS_PIN); + + tfmr =SSC_BF(TFMR_FSLEN_EXT, fslen_ext) + | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_NEGATIVE) + | SSC_BF(TFMR_FSDEN, 0) + | SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE) + | SSC_BF(TFMR_FSLEN, fslen) + | SSC_BF(TFMR_DATNB, (channels - 1)) + | SSC_BIT(TFMR_MSBF) + | SSC_BF(TFMR_DATDEF, 0) + | SSC_BF(TFMR_DATLEN, (bits - 1)); + break; + case SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_CBS_CFS: /* * DSP/PCM Mode A format, SSC provides BCLK and LRC clocks. -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 1/2] ASoC: pcm512x: Fixup warning splat
From: Peter Rosin p...@axentia.se Reported-by: kbuild test robot fengguang...@intel.com Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 51b279e3f465..067d11743c31 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -1266,7 +1266,6 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) { struct pcm512x_priv *pcm512x; int i, ret; - u32 val; pcm512x = devm_kzalloc(dev, sizeof(struct pcm512x_priv), GFP_KERNEL); if (!pcm512x) @@ -1347,6 +1346,7 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) #ifdef CONFIG_OF if (dev-of_node) { const struct device_node *np = dev-of_node; + u32 val; if (of_property_read_u32(np, pll-in, val) = 0) { if (val 6) { -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 2/2] ASoC: pcm512x: Use the correct range constraints for S24_LE
From: Peter Rosin p...@axentia.se This was overlooked in the late change to remove the I2S padding bits from S24_LE mode. The patch also limits S32_LE mode to 384kHz, the maximum according to the datasheets. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 39 --- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 067d11743c31..884784fb1566 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -356,36 +356,37 @@ static const struct snd_pcm_hw_constraint_list constraints_slave = { .list = pcm512x_dai_rates, }; -static const struct snd_interval pcm512x_dai_ranges_64bpf[] = { - { - .min = 8000, - .max = 195312, - }, { - .min = 25, - .max = 390625, - }, -}; - -static struct snd_pcm_hw_constraint_ranges constraints_64bpf = { - .count = ARRAY_SIZE(pcm512x_dai_ranges_64bpf), - .ranges = pcm512x_dai_ranges_64bpf, -}; - static int pcm512x_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) { - struct snd_pcm_hw_constraint_ranges *r = rule-private; + struct snd_interval ranges[2]; int frame_size; frame_size = snd_soc_params_to_frame_size(params); if (frame_size 0) return frame_size; - if (frame_size != 64) + switch (frame_size) { + case 32: + /* No hole when the frame size is 32. */ return 0; + case 48: + case 64: + /* There is only one hole in the range of supported +* rates, but it moves with the frame size. +*/ + memset(ranges, 0, sizeof(ranges)); + ranges[0].min = 8000; + ranges[0].max = 2500 / frame_size / 2; + ranges[1].min = DIV_ROUND_UP(1600, frame_size); + ranges[1].max = 384000; + break; + default: + return -EINVAL; + } return snd_interval_ranges(hw_param_interval(params, rule-var), - r-count, r-ranges, r-mask); + ARRAY_SIZE(ranges), ranges, 0); } static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, @@ -407,7 +408,7 @@ static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, return snd_pcm_hw_rule_add(substream-runtime, 0, SNDRV_PCM_HW_PARAM_RATE, pcm512x_hw_rule_rate, - (void *)constraints_64bpf, + NULL, SNDRV_PCM_HW_PARAM_FRAME_BITS, SNDRV_PCM_HW_PARAM_CHANNELS, -1); -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 0/2] ASoC: pcm512x: Fixups for the Clock master modes series
From: Peter Rosin p...@axentia.se These fixups can either be squashed in with 5/7 from the Clock master mode series, or they can be added on top of the current topic/pcm512x. Sorry for the trouble. I can squash them, and resend the Clock master modes series if that helps. Let me know how you'd like it. Cheers, Peter Peter Rosin (2): ASoC: pcm512x: Fixup warning splat ASoC: pcm512x: Use the correct range constraints for S24_LE sound/soc/codecs/pcm512x.c | 41 + 1 file changed, 21 insertions(+), 20 deletions(-) -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH] ASoC: pcm512x: Fix DSP program selection
From: Peter Rosin p...@axentia.se The DSP programs are listed out of order. Signed-off-by: Peter Rosin p...@axentia.se Cc: sta...@vger.kernel.org --- sound/soc/codecs/pcm512x.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 0c8aefab404c..640c99198cda 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -188,8 +188,8 @@ static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); static const char * const pcm512x_dsp_program_texts[] = { FIR interpolation with de-emphasis, Low latency IIR with de-emphasis, - Fixed process flow, High attenuation with de-emphasis, + Fixed process flow, Ringing-less low latency FIR, }; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH RESEND] pm: at91: Workaround DDRSDRC self-refresh bug with LPDDR1 memories
From: Peter Rosin p...@axentia.se The DDRSDR controller (on the ATSAMA5D31) fails miserably to put LPDDR1 memories in self-refresh. Force the controller to think it has DDR2 memories during the self-refresh period, as the DDR2 self-refresh spec is equivalent to LPDDR1, and is correctly implemented in the controller. Assume that the second controller has the same fault, and that other CPUs in the family has the same problem, but that is untested. Signed-off-by: Peter Rosin p...@axentia.se --- arch/arm/mach-at91/pm_slowclock.S | 43 +++- include/soc/at91/at91sam9_ddrsdr.h |2 +- 2 files changed, 39 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-at91/pm_slowclock.S b/arch/arm/mach-at91/pm_slowclock.S index 20018779bae7..63a9e0b0a17d 100644 --- a/arch/arm/mach-at91/pm_slowclock.S +++ b/arch/arm/mach-at91/pm_slowclock.S @@ -143,6 +143,16 @@ ddr_sr_enable: cmp memctrl, #AT91_MEMCTRL_DDRSDR bne sdr_sr_enable + /* LPDDR1 -- force DDR2 mode during self-refresh */ + ldr tmp1, [sdramc, #AT91_DDRSDRC_MDR] + str tmp1, .saved_sam9_mdr + bic tmp1, tmp1, #~AT91_DDRSDRC_MD + cmp tmp1, #AT91_DDRSDRC_MD_LOW_POWER_DDR + ldreq tmp1, [sdramc, #AT91_DDRSDRC_MDR] + biceq tmp1, tmp1, #AT91_DDRSDRC_MD + orreq tmp1, tmp1, #AT91_DDRSDRC_MD_DDR2 + streq tmp1, [sdramc, #AT91_DDRSDRC_MDR] + /* prepare for DDRAM self-refresh mode */ ldr tmp1, [sdramc, #AT91_DDRSDRC_LPR] str tmp1, .saved_sam9_lpr @@ -151,14 +161,26 @@ ddr_sr_enable: /* figure out if we use the second ram controller */ cmp ramc1, #0 - ldrne tmp2, [ramc1, #AT91_DDRSDRC_LPR] - strne tmp2, .saved_sam9_lpr1 - bicne tmp2, #AT91_DDRSDRC_LPCB - orrne tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH + beq ddr_no_2nd_ctrl + + ldr tmp2, [ramc1, #AT91_DDRSDRC_MDR] + str tmp2, .saved_sam9_mdr1 + bic tmp2, tmp2, #~AT91_DDRSDRC_MD + cmp tmp2, #AT91_DDRSDRC_MD_LOW_POWER_DDR + ldreq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + biceq tmp2, tmp2, #AT91_DDRSDRC_MD + orreq tmp2, tmp2, #AT91_DDRSDRC_MD_DDR2 + streq tmp2, [ramc1, #AT91_DDRSDRC_MDR] + + ldr tmp2, [ramc1, #AT91_DDRSDRC_LPR] + str tmp2, .saved_sam9_lpr1 + bic tmp2, #AT91_DDRSDRC_LPCB + orr tmp2, #AT91_DDRSDRC_LPCB_SELF_REFRESH /* Enable DDRAM self-refresh mode */ + str tmp2, [ramc1, #AT91_DDRSDRC_LPR] +ddr_no_2nd_ctrl: str tmp1, [sdramc, #AT91_DDRSDRC_LPR] - strne tmp2, [ramc1, #AT91_DDRSDRC_LPR] b sdr_sr_done @@ -289,12 +311,17 @@ sdr_sr_done: */ cmp memctrl, #AT91_MEMCTRL_DDRSDR bne sdr_en_restore + /* Restore MDR in case of LPDDR1 */ + ldr tmp1, .saved_sam9_mdr + str tmp1, [sdramc, #AT91_DDRSDRC_MDR] /* Restore LPR on AT91 with DDRAM */ ldr tmp1, .saved_sam9_lpr str tmp1, [sdramc, #AT91_DDRSDRC_LPR] /* if we use the second ram controller */ cmp ramc1, #0 + ldrne tmp2, .saved_sam9_mdr1 + strne tmp2, [ramc1, #AT91_DDRSDRC_MDR] ldrne tmp2, .saved_sam9_lpr1 strne tmp2, [ramc1, #AT91_DDRSDRC_LPR] @@ -328,5 +355,11 @@ ram_restored: .saved_sam9_lpr1: .word 0 +.saved_sam9_mdr: + .word 0 + +.saved_sam9_mdr1: + .word 0 + ENTRY(at91_slow_clock_sz) .word .-at91_slow_clock diff --git a/include/soc/at91/at91sam9_ddrsdr.h b/include/soc/at91/at91sam9_ddrsdr.h index 0210797abf2e..cd2c18787833 100644 --- a/include/soc/at91/at91sam9_ddrsdr.h +++ b/include/soc/at91/at91sam9_ddrsdr.h @@ -92,7 +92,7 @@ #defineAT91_DDRSDRC_UPD_MR (3 20)/* Update load mode register and extended mode register */ #define AT91_DDRSDRC_MDR 0x20/* Memory Device Register */ -#defineAT91_DDRSDRC_MD (3 0)/* Memory Device Type */ +#defineAT91_DDRSDRC_MD (7 0)/* Memory Device Type */ #defineAT91_DDRSDRC_MD_SDR 0 #defineAT91_DDRSDRC_MD_LOW_POWER_SDR 1 #defineAT91_DDRSDRC_MD_LOW_POWER_DDR 3 -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 1/7] ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges()
From: Peter Rosin p...@axentia.se Add helper functions to allow drivers to specify several disjoint ranges for a variable. In particular, there is a codec (PCM512x) that has a hole in its supported range of rates, due to PLL and divider restrictions. Signed-off-by: Peter Rosin p...@axentia.se --- include/sound/pcm.h | 12 +++ sound/core/pcm_lib.c | 85 ++ 2 files changed, 97 insertions(+) diff --git a/include/sound/pcm.h b/include/sound/pcm.h index b429b73e875e..95d1c20fa659 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -275,6 +275,12 @@ struct snd_pcm_hw_constraint_list { unsigned int mask; }; +struct snd_pcm_hw_constraint_ranges { + unsigned int count; + const struct snd_interval *ranges; + unsigned int mask; +}; + struct snd_pcm_hwptr_log; struct snd_pcm_runtime { @@ -910,6 +916,8 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k, const struct snd_interval *b, struct snd_interval *c); int snd_interval_list(struct snd_interval *i, unsigned int count, const unsigned int *list, unsigned int mask); +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *list, unsigned int mask); int snd_interval_ratnum(struct snd_interval *i, unsigned int rats_count, struct snd_ratnum *rats, unsigned int *nump, unsigned int *denp); @@ -934,6 +942,10 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, const struct snd_pcm_hw_constraint_list *l); +int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime, +unsigned int cond, +snd_pcm_hw_param_t var, +const struct snd_pcm_hw_constraint_ranges *r); int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, unsigned int cond, snd_pcm_hw_param_t var, diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index ec9e7866177f..446c00bd908b 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1015,6 +1015,60 @@ int snd_interval_list(struct snd_interval *i, unsigned int count, EXPORT_SYMBOL(snd_interval_list); +/** + * snd_interval_ranges - refine the interval value from the list of ranges + * @i: the interval value to refine + * @count: the number of elements in the list of ranges + * @ranges: the ranges list + * @mask: the bit-mask to evaluate + * + * Refines the interval value from the list of ranges. + * When mask is non-zero, only the elements corresponding to bit 1 are + * evaluated. + * + * Return: Positive if the value is changed, zero if it's not changed, or a + * negative error code. + */ +int snd_interval_ranges(struct snd_interval *i, unsigned int count, + const struct snd_interval *ranges, unsigned int mask) +{ + unsigned int k; + struct snd_interval range_union; + struct snd_interval range; + + if (!count) { + snd_interval_none(i); + return -EINVAL; + } + snd_interval_any(range_union); + range_union.min = UINT_MAX; + range_union.max = 0; + for (k = 0; k count; k++) { + if (mask !(mask (1 k))) + continue; + snd_interval_copy(range, ranges[k]); + if (snd_interval_refine(range, i) 0) + continue; + if (snd_interval_empty(range)) + continue; + + if (range.min range_union.min) { + range_union.min = range.min; + range_union.openmin = 1; + } + if (range.min == range_union.min !range.openmin) + range_union.openmin = 0; + if (range.max range_union.max) { + range_union.max = range.max; + range_union.openmax = 1; + } + if (range.max == range_union.max !range.openmax) + range_union.openmax = 0; + } + return snd_interval_refine(i, range_union); +} +EXPORT_SYMBOL(snd_interval_ranges); + static int snd_interval_step(struct snd_interval *i, unsigned int step) { unsigned int n; @@ -1221,6 +1275,37 @@ int snd_pcm_hw_constraint_list(struct snd_pcm_runtime *runtime, EXPORT_SYMBOL(snd_pcm_hw_constraint_list); +static int snd_pcm_hw_rule_ranges(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_pcm_hw_constraint_ranges *r = rule-private; + return snd_interval_ranges(hw_param_interval(params, rule-var
[PATCH 2/7] ASoC: pcm512x: Fix spelling of register field names.
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- sound/soc/codecs/pcm512x.h |6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 30c673cdc12e..355a8543c8b1 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -277,7 +277,7 @@ SOC_ENUM(Auto Mute Time Right, pcm512x_autom_r), SOC_SINGLE(Auto Mute Mono Switch, PCM512x_DIGITAL_MUTE_3, PCM512x_ACTL_SHIFT, 1, 0), SOC_DOUBLE(Auto Mute Switch, PCM512x_DIGITAL_MUTE_3, PCM512x_AMLE_SHIFT, - PCM512x_AMLR_SHIFT, 1, 0), + PCM512x_AMRE_SHIFT, 1, 0), SOC_ENUM(Volume Ramp Down Rate, pcm512x_vndf), SOC_ENUM(Volume Ramp Down Step, pcm512x_vnds), diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index 6ee76aaca09a..28b3dfd302bc 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -108,8 +108,8 @@ #define PCM512x_RQML_SHIFT 4 /* Page 0, Register 4 - PLL */ -#define PCM512x_PLCE (1 0) -#define PCM512x_RLCE_SHIFT 0 +#define PCM512x_PLLE (1 0) +#define PCM512x_PLLE_SHIFT 0 #define PCM512x_PLCK (1 4) #define PCM512x_PLCK_SHIFT 4 @@ -152,7 +152,7 @@ /* Page 0, Register 65 - Digital mute enables */ #define PCM512x_ACTL_SHIFT 2 #define PCM512x_AMLE_SHIFT 1 -#define PCM512x_AMLR_SHIFT 0 +#define PCM512x_AMRE_SHIFT 0 /* Page 1, Register 2 - analog volume control */ #define PCM512x_RAGN_SHIFT 0 -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 4/7] ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL
From: Peter Rosin p...@axentia.se Use register field names from the seemingly compatible PCM5242 datasheet, as the PCM512x and PCM514x datasheets are severly lacking. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 452 ++-- sound/soc/codecs/pcm512x.h | 57 +- 2 files changed, 492 insertions(+), 17 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 78dc3306d2f2..70a88c7d2b1d 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -23,6 +23,7 @@ #include linux/regulator/consumer.h #include sound/soc.h #include sound/soc-dapm.h +#include sound/pcm_params.h #include sound/tlv.h #include pcm512x.h @@ -39,6 +40,7 @@ struct pcm512x_priv { struct clk *sclk; struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; + int fmt; }; /* @@ -69,6 +71,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_MUTE, 0x00 }, { PCM512x_DSP, 0x00 }, { PCM512x_PLL_REF, 0x00 }, + { PCM512x_DAC_REF, 0x00 }, { PCM512x_DAC_ROUTING, 0x11 }, { PCM512x_DSP_PROGRAM, 0x01 }, { PCM512x_CLKDET,0x00 }, @@ -87,6 +90,18 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_ANALOG_GAIN_BOOST, 0x00 }, { PCM512x_VCOM_CTRL_1, 0x00 }, { PCM512x_VCOM_CTRL_2, 0x01 }, + { PCM512x_BCLK_LRCLK_CFG,0x00 }, + { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_DSP_CLKDIV,0x00 }, + { PCM512x_DAC_CLKDIV,0x00 }, + { PCM512x_NCP_CLKDIV,0x00 }, + { PCM512x_OSR_CLKDIV,0x00 }, + { PCM512x_MASTER_CLKDIV_1, 0x00 }, + { PCM512x_MASTER_CLKDIV_2, 0x00 }, + { PCM512x_FS_SPEED_MODE, 0x00 }, + { PCM512x_IDAC_1,0x01 }, + { PCM512x_IDAC_2,0x00 }, }; static bool pcm512x_readable(struct device *dev, unsigned int reg) @@ -103,6 +118,8 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_DSP_GPIO_INPUT: case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: + case PCM512x_DAC_REF: + case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: case PCM512x_PLL_COEFF_2: @@ -303,6 +320,105 @@ static const struct snd_soc_dapm_route pcm512x_dapm_routes[] = { { OUTR, NULL, DACR }, }; +static const u32 pcm512x_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, 384000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_slave = { + .count = ARRAY_SIZE(pcm512x_dai_rates), + .list = pcm512x_dai_rates, +}; + +static int pcm512x_params_to_frame_size(struct snd_pcm_hw_params *params) +{ + int sample_size; + + sample_size = snd_pcm_format_physical_width(params_format(params)); + if (sample_size 0) + return sample_size; + + return snd_soc_calc_frame_size(sample_size, params_channels(params), 1); +} + +static int pcm512x_dai_startup_master(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device *dev = dai-dev; + struct snd_pcm_hw_constraint_ratnums *constraints_no_pll; + struct snd_ratnum *rats_no_pll; + + if (IS_ERR(pcm512x-sclk)) { + dev_err(dev, Need SCLK for master mode: %ld\n, + PTR_ERR(pcm512x-sclk)); + return PTR_ERR(pcm512x-sclk); + } + + constraints_no_pll = devm_kzalloc(dev, sizeof(*constraints_no_pll), + GFP_KERNEL); + if (!constraints_no_pll) + return -ENOMEM; + constraints_no_pll-nrats = 1; + rats_no_pll = devm_kzalloc(dev, sizeof(*rats_no_pll), GFP_KERNEL); + if (!rats_no_pll) + return -ENOMEM; + constraints_no_pll-rats = rats_no_pll; + rats_no_pll-num = clk_get_rate(pcm512x-sclk) / 64; + rats_no_pll-den_min = 1; + rats_no_pll-den_max = 128; + rats_no_pll-den_step = 1; + + return snd_pcm_hw_constraint_ratnums(substream-runtime, 0, +SNDRV_PCM_HW_PARAM_RATE, +constraints_no_pll); +} + +static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, +struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + struct device
[PATCH 5/7] ASoC: pcm512x: Support mastering BCLK/LRCLK using the PLL
From: Peter Rosin p...@axentia.se Using the PLL in master mode requires using an external connection between one of the GPIO pins (configured as PLL/4 output) and the SCK pin. It also requires the external clock to be fed to some other GPIO pin instead of the SCK pin. This is described for the PCM5122 chip in the answers to the forum post PCM5122 DAC as I2S master troubles with PLL mode at the TI E2E community pages (1). The clocking functionality is also much better described in the datasheet for the chip PCM5242, which seems to be register compatible with PCM512x and PCM514x (which both have severely lacking datasheets). (1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/267830 Signed-off-by: Peter Rosin p...@axentia.se --- .../devicetree/bindings/sound/pcm512x.txt | 25 +- sound/soc/codecs/pcm512x.c | 469 +++- sound/soc/codecs/pcm512x.h | 44 +- 3 files changed, 512 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 98e0d34915e8..3aae3b41bd8e 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -17,9 +17,16 @@ Required properties: Optional properties: - clocks : A clock specifier for the clock connected as SCLK. If this -is absent the device will be configured to clock from BCLK. +is absent the device will be configured to clock from BCLK. If pll-in +and pll-out are specified in addition to a clock, the device is +configured to accept clock input on a specified gpio pin. -Example: + - pll-in, pll-out : gpio pins used to connect the pll using 1 +through 6. The device will be configured for clock input on the +given pll-in pin and PLL output on the given pll-out pin. An +external connection from the pll-out pin to the SCLK pin is assumed. + +Examples: pcm5122: pcm5122@4c { compatible = ti,pcm5122; @@ -29,3 +36,17 @@ Example: DVDD-supply = reg_1v8; CPVDD-supply = reg_3v3; }; + + + pcm5142: pcm5142@4c { + compatible = ti,pcm5142; + reg = 0x4c; + + AVDD-supply = reg_3v3_analog; + DVDD-supply = reg_1v8; + CPVDD-supply = reg_3v3; + + clocks = sck; + pll-in = 3; + pll-out = 6; + }; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 70a88c7d2b1d..df8948b4e848 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -21,6 +21,7 @@ #include linux/pm_runtime.h #include linux/regmap.h #include linux/regulator/consumer.h +#include linux/gcd.h #include sound/soc.h #include sound/soc-dapm.h #include sound/pcm_params.h @@ -28,6 +29,11 @@ #include pcm512x.h +#define DIV_ROUND_DOWN_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) +#define DIV_ROUND_CLOSEST_ULL(ll, d) \ + ({ unsigned long long _tmp = (ll)+(d)/2; do_div(_tmp, d); _tmp; }) + #define PCM512x_NUM_SUPPLIES 3 static const char * const pcm512x_supply_names[PCM512x_NUM_SUPPLIES] = { AVDD, @@ -41,6 +47,13 @@ struct pcm512x_priv { struct regulator_bulk_data supplies[PCM512x_NUM_SUPPLIES]; struct notifier_block supply_nb[PCM512x_NUM_SUPPLIES]; int fmt; + int pll_in; + int pll_out; + int pll_r; + int pll_j; + int pll_d; + int pll_p; + unsigned long real_pll; }; /* @@ -92,7 +105,13 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG,0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_PLLIN,0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, + { PCM512x_PLL_COEFF_0, 0x00 }, + { PCM512x_PLL_COEFF_1, 0x00 }, + { PCM512x_PLL_COEFF_2, 0x00 }, + { PCM512x_PLL_COEFF_3, 0x00 }, + { PCM512x_PLL_COEFF_4, 0x00 }, { PCM512x_DSP_CLKDIV,0x00 }, { PCM512x_DAC_CLKDIV,0x00 }, { PCM512x_NCP_CLKDIV,0x00 }, @@ -119,6 +138,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: case PCM512x_DAC_REF: + case PCM512x_GPIO_PLLIN: case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: case PCM512x_PLL_COEFF_1: @@ -160,6 +180,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_RATE_DET_2: case PCM512x_RATE_DET_3: case PCM512x_RATE_DET_4: + case PCM512x_CLOCK_STATUS: case PCM512x_ANALOG_MUTE_DET: case PCM512x_GPIN: case PCM512x_DIGITAL_MUTE_DET: @@ -171,6 +192,8 @@ static bool pcm512x_readable
[PATCH 7/7] ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 13 +++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 2a50c58a2fb1..511d98b3afa4 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -486,6 +486,7 @@ static int pcm512x_dai_startup(struct snd_pcm_substream *substream, switch (pcm512x-fmt SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: return pcm512x_dai_startup_master(substream, dai); case SND_SOC_DAIFMT_CBS_CFS: @@ -992,6 +993,8 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); int alen; int gpio; + int clock_output; + int master_mode; int ret; dev_dbg(codec-dev, hw_params %u Hz, %u channels\n, @@ -1040,6 +1043,12 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, } return 0; case SND_SOC_DAIFMT_CBM_CFM: + clock_output = PCM512x_BCKO | PCM512x_LRKO; + master_mode = PCM512x_RLRK | PCM512x_RBCK; + break; + case SND_SOC_DAIFMT_CBM_CFS: + clock_output = PCM512x_BCKO; + master_mode = PCM512x_RBCK; break; default: return -EINVAL; @@ -1136,7 +1145,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(pcm512x-regmap, PCM512x_BCLK_LRCLK_CFG, PCM512x_BCKP | PCM512x_BCKO | PCM512x_LRKO, -PCM512x_BCKO | PCM512x_LRKO); +clock_output); if (ret != 0) { dev_err(codec-dev, Failed to enable clock output: %d\n, ret); return ret; @@ -1144,7 +1153,7 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret = regmap_update_bits(pcm512x-regmap, PCM512x_MASTER_MODE, PCM512x_RLRK | PCM512x_RBCK, -PCM512x_RLRK | PCM512x_RBCK); +master_mode); if (ret != 0) { dev_err(codec-dev, Failed to enable master mode: %d\n, ret); return ret; -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 0/7] ASoC: pcm512x: Clock master modes
From: Peter Rosin p...@axentia.se Hi! This series implements BCLK master modes for the pcm512x driver. It has only been tested with the pcm5142 chip, but they are from the same family and should be compatible. I have mainly used the spec for the newer pcm5242 chip (also from the same family) as it fills in a lot of blanks in the pcm512x/pcm514x specs. The code has also seen most of its testing in a 3.10 environment, so there might be some forward-porting warts. But it is able to play sound in 3.18 as well, and most of the changes have little to do with anything but the clocking in the chip itself. Cheers, Peter Peter Rosin (7): ALSA: pcm: Add snd_interval_ranges() and snd_pcm_hw_constraint_ranges() ASoC: pcm512x: Fix spelling of register field names. ASoC: pcm512x: Change register default to match actual content after reset ASoC: pcm512x: Support mastering BCLK/LRCLK without using the PLL ASoC: pcm512x: Support mastering BCLK/LRCLK using the PLL ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible ASoC: pcm512x: Support SND_SOC_DAIFMT_CBM_CFS .../devicetree/bindings/sound/pcm512x.txt | 25 +- include/sound/pcm.h| 12 + sound/core/pcm_lib.c | 85 ++ sound/soc/codecs/pcm512x.c | 955 +++- sound/soc/codecs/pcm512x.h | 109 ++- 5 files changed, 1161 insertions(+), 25 deletions(-) -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH 6/7] ASoC: pcm512x: Avoid the PLL for the DAC clock, if possible
From: Peter Rosin p...@axentia.se The PLL introduces jitter, which in turn introduces noice if used to clock the DAC. Thus, avoid the PLL output, and use the PLL input to drive the DAC clock, if possible. This is described for the PCM5142/PCM5242 chips in the answers to the forum post PCM5142/PCM5242 DAC clock source at the TI E2E community pages (1). (1) http://e2e.ti.com/support/data_converters/audio_converters/f/64/t/389994 Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 119 ++-- sound/soc/codecs/pcm512x.h |4 +- 2 files changed, 96 insertions(+), 27 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index df8948b4e848..2a50c58a2fb1 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -105,6 +105,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_VCOM_CTRL_2, 0x01 }, { PCM512x_BCLK_LRCLK_CFG,0x00 }, { PCM512x_MASTER_MODE, 0x7c }, + { PCM512x_GPIO_DACIN,0x00 }, { PCM512x_GPIO_PLLIN,0x00 }, { PCM512x_SYNCHRONIZE, 0x10 }, { PCM512x_PLL_COEFF_0, 0x00 }, @@ -138,6 +139,7 @@ static bool pcm512x_readable(struct device *dev, unsigned int reg) case PCM512x_MASTER_MODE: case PCM512x_PLL_REF: case PCM512x_DAC_REF: + case PCM512x_GPIO_DACIN: case PCM512x_GPIO_PLLIN: case PCM512x_SYNCHRONIZE: case PCM512x_PLL_COEFF_0: @@ -681,6 +683,37 @@ done: return 0; } +static unsigned long pcm512x_pllin_dac_rate(struct snd_soc_dai *dai, + unsigned long osr_rate, + unsigned long pllin_rate) +{ + struct snd_soc_codec *codec = dai-codec; + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + unsigned long dac_rate; + + if (!pcm512x-pll_out) + return 0; /* no PLL to bypass, force SCK as DAC input */ + + if (pllin_rate % osr_rate) + return 0; /* futile, quit early */ + + /* run DAC no faster than 6144000 Hz */ + for (dac_rate = rounddown(6144000, osr_rate); +dac_rate; +dac_rate -= osr_rate) { + + if (pllin_rate / dac_rate 128) + return 0; /* DAC divider would be too big */ + + if (!(pllin_rate % dac_rate)) + return dac_rate; + + dac_rate -= osr_rate; + } + + return 0; +} + static int pcm512x_set_dividers(struct snd_soc_dai *dai, struct snd_pcm_hw_params *params) { @@ -694,6 +727,7 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, unsigned long bclk_rate; unsigned long sample_rate; unsigned long osr_rate; + unsigned long dacsrc_rate; int bclk_div; int lrclk_div; int dsp_div; @@ -701,11 +735,10 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, unsigned long dac_rate; int ncp_div; int osr_div; - unsigned long dac_mul; - unsigned long sck_mul; int ret; int idac; int fssp; + int gpio; lrclk_div = pcm512x_params_to_frame_size(params); if (lrclk_div == 0) { @@ -794,31 +827,72 @@ static int pcm512x_set_dividers(struct snd_soc_dai *dai, /* run DSP no faster than 50 MHz */ dsp_div = mck_rate 5000 ? 2 : 1; - /* run DAC no faster than 6144000 Hz */ - dac_mul = 6144000 / osr_rate; - sck_mul = sck_rate / osr_rate; - for (; dac_mul; dac_mul--) { - if (!(sck_mul % dac_mul)) - break; - } - if (!dac_mul) { - dev_err(dev, Failed to find DAC rate\n); - return -EINVAL; - } + dac_rate = pcm512x_pllin_dac_rate(dai, osr_rate, pllin_rate); + if (dac_rate) { + /* the desired clock rate is compatible with the pll input +* clock, so use that clock as dac input instead of the pll +* output clock since the pll will introduce jitter and thus +* noise. +*/ + dev_dbg(dev, using pll input as dac input\n); + ret = regmap_update_bits(pcm512x-regmap, PCM512x_DAC_REF, +PCM512x_SDAC, PCM512x_SDAC_GPIO); + if (ret != 0) { + dev_err(codec-dev, + Failed to set gpio as dacref: %d\n, ret); + return ret; + } - dac_rate = dac_mul * osr_rate; - dev_dbg(dev, dac_rate %lu sample_rate %lu\n, dac_rate, sample_rate); + gpio = PCM512x_GREF_GPIO1 + pcm512x-pll_in - 1; + ret = regmap_update_bits(pcm512x-regmap, PCM512x_GPIO_DACIN
[PATCH 3/7] ASoC: pcm512x: Change register default to match actual content after reset
From: Peter Rosin p...@axentia.se Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c |2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 355a8543c8b1..78dc3306d2f2 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -78,7 +78,7 @@ static const struct reg_default pcm512x_reg_defaults[] = { { PCM512x_DIGITAL_VOLUME_2, 0x30 }, { PCM512x_DIGITAL_VOLUME_3, 0x30 }, { PCM512x_DIGITAL_MUTE_1,0x22 }, - { PCM512x_DIGITAL_MUTE_2,0x00 }, + { PCM512x_DIGITAL_MUTE_2,0x02 }, { PCM512x_DIGITAL_MUTE_3,0x07 }, { PCM512x_OUTPUT_AMPLITUDE, 0x00 }, { PCM512x_ANALOG_GAIN_CTRL, 0x00 }, -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v3] ASoC: atmel_ssc_dai: Allow more rates
From: Peter Rosin p...@axentia.se When the SSC acts as BCK master, use a ratnum rule to limit the rate instead of only doing the standard rates. When the SSC acts as BCK slave, allow any BCK frequency up to the SSC master clock, divided by either of 2, 3 or 6. Put a cap at 384kHz. Who's /ever/ going to need more than that? The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio Considerations section from the SSC documentation: The Transmitter and the Receiver can be programmed to operate with the clock signals provided on either the TK or RK pins. This allows the SSC to support many slave-mode data transfers. In this case, the maximum clock speed allowed on the RK pin is: - Peripheral clock divided by 2 if Receiver Frame Synchro is input - Peripheral clock divided by 3 if Receiver Frame Synchro is output In addition, the maximum clock speed allowed on the TK pin is: - Peripheral clock divided by 6 if Transmit Frame Synchro is input - Peripheral clock divided by 2 if Transmit Frame Synchro is output Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 111 +-- sound/soc/atmel/atmel_ssc_dai.h |1 + 2 files changed, 108 insertions(+), 4 deletions(-) Changes since v2: - Killed the 500ppm reduction. Just trust the nominal clock rates of the given clocks. - Don't multiply the given SSC clk rate by two to get to the SSC master clock rate. That factor was the result of some confusion on my part. The SSC clk *is* the SSC master clock. Changes since v1: - I have checked all Atmel datasheets I could get my hands on (and that seemed relevant), and in every instance where they have described the SSC, the description have the exact rate limitations on the RK and TK pin that I have implemented here. - Improved the comments. - Rebased on top of the latest patches from Bo Chen. diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 379ac2a6ab16..6b8e64899205 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -187,6 +187,94 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * When the bit clock is input, limit the maximum rate according to the + * Serial Clock Ratio Considerations section from the SSC documentation: + * + * The Transmitter and the Receiver can be programmed to operate + * with the clock signals provided on either the TK or RK pins. + * This allows the SSC to support many slave-mode data transfers. + * In this case, the maximum clock speed allowed on the RK pin is: + * - Peripheral clock divided by 2 if Receiver Frame Synchro is input + * - Peripheral clock divided by 3 if Receiver Frame Synchro is output + * In addition, the maximum clock speed allowed on the TK pin is: + * - Peripheral clock divided by 6 if Transmit Frame Synchro is input + * - Peripheral clock divided by 2 if Transmit Frame Synchro is output + * + * When the bit clock is output, limit the rate according to the + * SSC divider restrictions. + */ +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct atmel_ssc_info *ssc_p = rule-private; + struct ssc_device *ssc = ssc_p-ssc; + struct snd_interval *i = hw_param_interval(params, rule-var); + struct snd_interval t; + struct snd_ratnum r = { + .den_min = 1, + .den_max = 4095, + .den_step = 1, + }; + unsigned int num = 0, den = 0; + int frame_size; + int mck_div = 2; + int ret; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size 0) + return frame_size; + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + if ((ssc_p-dir_mask SSC_DIR_MASK_CAPTURE) +ssc-clk_from_rk_pin) + /* Receiver Frame Synchro (i.e. capture) +* is output (format is _CFS) and the RK pin +* is used for input (format is _CBM_). +*/ + mck_div = 3; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + if ((ssc_p-dir_mask SSC_DIR_MASK_PLAYBACK) +!ssc-clk_from_rk_pin) + /* Transmit Frame Synchro (i.e. playback) +* is input (format is _CFM) and the TK pin +* is used for input (format _CBM_ but not +* using the RK pin). +*/ + mck_div = 6; + break; + } + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + r.num = ssc_p-mck_rate / mck_div / frame_size
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Bo Shen wrote: Hi Peter, Hi! On 02/04/2015 07:52 PM, Peter Rosin wrote: From: Peter Rosin p...@axentia.se When the SSC acts as BCK master, use a ratnum rule to limit the rate instead of only doing the standard rates. When the SSC acts as BCK slave, allow any BCK frequency up to within 500ppm of the SSC master clock, possibly divided by 2, 3 or 6. Put a cap at 384kHz. Who's /ever/ going to need more than that? The divider of 2, 3 or 6 is selected based on the Serial Clock Ratio Considerations section from the SSC documentation: The Transmitter and the Receiver can be programmed to operate with the clock signals provided on either the TK or RK pins. This allows the SSC to support many slave-mode data transfers. In this case, the maximum clock speed allowed on the RK pin is: - Peripheral clock divided by 2 if Receiver Frame Synchro is input - Peripheral clock divided by 3 if Receiver Frame Synchro is output In addition, the maximum clock speed allowed on the TK pin is: - Peripheral clock divided by 6 if Transmit Frame Synchro is input - Peripheral clock divided by 2 if Transmit Frame Synchro is output Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 113 +-- sound/soc/atmel/atmel_ssc_dai.h |1 + 2 files changed, 110 insertions(+), 4 deletions(-) Changes since v1: - I have checked all Atmel datasheets I could get my hands on (and that seemed relevant), and in every instance where they have described the SSC, the description have the exact rate limitations on the RK and TK pin that I have implemented here. - Improved the comments. - Rebased on top of the latest patches from Bo Chen. One thing remains a bit unclear, and that is the 500ppm deduction. Is that really warranted? The number was just pulled out of my hat... Cheers, Peter diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 379ac2a6ab16..767f65bab25d 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -187,6 +187,96 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +/* + * When the bit clock is input, limit the maximum rate according to +the + * Serial Clock Ratio Considerations section from the SSC documentation: + * + * The Transmitter and the Receiver can be programmed to operate + * with the clock signals provided on either the TK or RK pins. + * This allows the SSC to support many slave-mode data transfers. + * In this case, the maximum clock speed allowed on the RK pin is: + * - Peripheral clock divided by 2 if Receiver Frame Synchro is input + * - Peripheral clock divided by 3 if Receiver Frame Synchro is output + * In addition, the maximum clock speed allowed on the TK pin is: + * - Peripheral clock divided by 6 if Transmit Frame Synchro is input + * - Peripheral clock divided by 2 if Transmit Frame Synchro is output + * + * When the bit clock is output, limit the rate according to the + * SSC divider restrictions. + */ +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct atmel_ssc_info *ssc_p = rule-private; + struct ssc_device *ssc = ssc_p-ssc; + struct snd_interval *i = hw_param_interval(params, rule-var); + struct snd_interval t; + struct snd_ratnum r = { + .den_min = 1, + .den_max = 4095, + .den_step = 1, + }; + unsigned int num = 0, den = 0; + int frame_size; + int mck_div = 2; + int ret; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size 0) + return frame_size; + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + if ((ssc_p-dir_mask SSC_DIR_MASK_CAPTURE) +ssc-clk_from_rk_pin) + /* Receiver Frame Synchro (i.e. capture) +* is output (format is _CFS) and the RK pin +* is used for input (format is _CBM_). +*/ + mck_div = 3; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + if ((ssc_p-dir_mask SSC_DIR_MASK_PLAYBACK) +!ssc-clk_from_rk_pin) + /* Transmit Frame Synchro (i.e. playback) +* is input (format is _CFM) and the TK pin +* is used for input (format _CBM_ but not +* using the RK pin). +*/ + mck_div = 6; + break; + } + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + r.num = ssc_p-mck_rate / mck_div
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Bo Shen wrote: Hi Peter, On 02/09/2015 03:35 PM, Peter Rosin wrote: Bo Shen wrote: Hi Peter, Hi! On 02/07/2015 06:51 PM, Peter Rosin wrote: Mark Brown wrote: On Wed, Feb 04, 2015 at 12:52:25PM +0100, Peter Rosin wrote: One thing remains a bit unclear, and that is the 500ppm deduction. Is that really warranted? The number was just pulled out of my hat... I don't really get what this is supposed to be protecting against. + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + t.min = 8000; + t.max = ssc_p-mck_rate / mck_div / frame_size; + /* Take away 500ppm, just to be on the safe side. */ + t.max -= t.max / 2000; + t.openmin = t.openmax = 0; + t.integer = 0; + ret = snd_interval_refine(i, t); As I understand it this is a straight divider rather than something that's doing dithering or anything else more fancy. Given that it seems as well just to trust the clock rate we've got - we don't do any error tracking with the clock API (perhaps we should) and for many applications some degree of divergence from the nominal rate is not *too* bad for audio systems (for application specific values of some and too of course). If it is just dividers I'm not sure the situation is really improved materially by knocking off the top frequency. If we are doing something more fancy than divididing my analysis is off base of course. I'm thinking that the SSC samples the selected BCK pin using the (possibly divided) peripheral clock. Getting too near the theoretical rate limit would be bad, if these two independent clocks drift the wrong way. At least that is my take on it, but I don't know the internal workings of the SSC, so... I was hoping that someone from Atmel could chime in? Maybe I'm totally Sorry for late response. No problem! off base, and the SSC is doing this completely differently? What you mean here? I am not sure I fully understand. The SSC spec list a maximum rate (which varies with the direction of various signals, ignoring that for the sake of this explanation). Lets assume that this maximum rate is 11MHz, derived from the peripheral clock which might be 66MHz. If you then try to input an 11MHz signal derived from some unrelated xtal you might think it should work. My theory was that the rate limit would be broken if the peripheral clock wasn't really 66MHz, but instead a few ppm lower than nominal, and the unrelated xtal was a few ppm higher than nominal. If this matters or not depends on how the SSC is implemented. This is to let the user to know the clock limitation, am I right? Yes, sort of, to prevent the user from even attempting to go too near the nominal limit. And at the same time deal with the un-precise clock which come to SSC? If this case, I think we should trust the clock come to SSC. Ok, I'll just kill the 500ppm thing for the next round. I'll wait a bit for the discussion in the other branch to fade out though. :-) Cheers, Peter There might be other reasons for not caring all that much about this fringe case, and just trust the nominal rates and limits. In our application, we're not near the limit. Therefore, it really doesn't matter much to us. Should I resend w/o the 500ppm deduction? Cheers, Peter Best Regards, Bo Shen Best Regards, Bo Shen -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Bo Shen wrote: Hi Peter, Hi! On 02/09/2015 05:07 PM, Peter Rosin wrote: Bo Shen wrote: Hi Peter, On 02/09/2015 04:09 PM, Peter Rosin wrote: [Snip] /*-*\ * DAI functions @@ -200,6 +290,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct atmel_ssc_info *ssc_p = ssc_info[dai-id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; + int ret; pr_debug(atmel_ssc_startup: SSC_SR=0x%u\n, ssc_readl(ssc_p-ssc-regs, SR)); @@ -207,6 +298,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, /* Enable PMC peripheral clock for this SSC */ pr_debug(atmel_ssc_dai: Starting clock\n); clk_enable(ssc_p-ssc-clk); + ssc_p-mck_rate = clk_get_rate(ssc_p-ssc-clk) * 2; Why the mck_rate is calculated in this form? What did you have in mind? Add another clock to the ssc node in the device tree? IIUC, the device tree (at least normally) has the ssc clk as the peripheral clock divided by 2, but the ssc specifies (when capturing in the CBM/CFS case) the rate limit as the peripheral clock divided by 3 (i.e. ssc clk / 1.5). Since the SSC spec expresses the rate limit in terms of the peripheral clock, this was what I came up with. I didn't want to require dt changes... You make a misunderstand for the mck for ssc peripheral. The mck here is not the system mck, it only related with the ssc, it is the PMC output. For example, in device tree, the ssc clock divided by 2, then the pmc output for ssc is system mck / 2, so the ssc mck is system mck / 2. If divided by 4, then the ssc mck is system / 4 I think the reason for my misunderstanding might be that in the 3.10-at91 tree, the ssc clk is twice the rate compared to what it is in the 3.18-at91 tree. This made me assume that the ssc clk had I think maybe you didn't use the latest linux-3.10-at91 code. In that code, we also divided by 2 in device tree. That's a rather confusing statement. The clock tree isn't even managed by the device tree in linux-3.10-at91. Sure, there's the 12MHz system master clock in sama5d3.dtsi, but that's about it. The rest of the clocks are in arch/arm/mach-at91/sama5d3.c. And the part about ssc0/ssc1 hasn't been updated since it was added in 3.9. What am I missing? been changed to mean the rate after the fixed divider by two that is activated as soon as the ssc clock divider (given by SSC_CMR) is activated, and that it was a simple matter of multiplying by two to get to the MCK rate. I further assumed that Master Clock in the Serial Clock Ratio Considerations section was this MCK. Maybe the mistake was to involve the peripheral clock at all? Ok, so I may have misunderstood, but in that case what does that mean in terms of finding the Master Clock rate that is mentioned in the Serial Clock Ratio Considerations section? Is it perhaps the rate of the parent clock of the given ssc clk? Or, given the above explanation, is it correct to simply multiply by two as I have done? The Master Clock actually is the same as Peripheral clock. Thanks for your information, I will send this to our datasheet team to update this part. You are still not saying how to get to the Master Clock rate (i.e. the Master Clock that is mentioned in the Serial Clock Ratio Considerations section. You are only implying that multiplying the ssc clock rate with 2 is wrong for some reason. Are you saying that the ssc clock is supposed to be this Master Clock? Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH v2] ASoC: atmel_ssc_dai: Allow more rates
Bo Shen write: Hi Peter, Hi! [Snip] /*-*\ * DAI functions @@ -200,6 +290,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct atmel_ssc_info *ssc_p = ssc_info[dai-id]; struct atmel_pcm_dma_params *dma_params; int dir, dir_mask; + int ret; pr_debug(atmel_ssc_startup: SSC_SR=0x%u\n, ssc_readl(ssc_p-ssc-regs, SR)); @@ -207,6 +298,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, /* Enable PMC peripheral clock for this SSC */ pr_debug(atmel_ssc_dai: Starting clock\n); clk_enable(ssc_p-ssc-clk); + ssc_p-mck_rate = clk_get_rate(ssc_p-ssc-clk) * 2; Why the mck_rate is calculated in this form? What did you have in mind? Add another clock to the ssc node in the device tree? IIUC, the device tree (at least normally) has the ssc clk as the peripheral clock divided by 2, but the ssc specifies (when capturing in the CBM/CFS case) the rate limit as the peripheral clock divided by 3 (i.e. ssc clk / 1.5). Since the SSC spec expresses the rate limit in terms of the peripheral clock, this was what I came up with. I didn't want to require dt changes... You make a misunderstand for the mck for ssc peripheral. The mck here is not the system mck, it only related with the ssc, it is the PMC output. For example, in device tree, the ssc clock divided by 2, then the pmc output for ssc is system mck / 2, so the ssc mck is system mck / 2. If divided by 4, then the ssc mck is system / 4 I think the reason for my misunderstanding might be that in the 3.10-at91 tree, the ssc clk is twice the rate compared to what it is in the 3.18-at91 tree. This made me assume that the ssc clk had I think maybe you didn't use the latest linux-3.10-at91 code. In that code, we also divided by 2 in device tree. That's a rather confusing statement. The clock tree isn't even managed by the device tree in linux-3.10-at91. Sure, there's the 12MHz system master clock in sama5d3.dtsi, but that's about it. The rest of the clocks are in arch/arm/mach-at91/sama5d3.c. And the part about ssc0/ssc1 hasn't been updated since it was added in 3.9. What am I missing? I am not sure what the kernel you are talking about now. In linux-3.10-at91 branch on github.com/linux4sam/linux-at91. In the arch/arm/mach-at91/sama5d3.c ---8--- static struct clk ssc0_clk = { .name = ssc0_clk, .pid= SAMA5D3_ID_SSC0, .type = CLK_TYPE_PERIPHERAL, .div= AT91_PMC_PCR_DIV2, }; static struct clk ssc1_clk = { .name = ssc1_clk, .pid= SAMA5D3_ID_SSC1, .type = CLK_TYPE_PERIPHERAL, .div= AT91_PMC_PCR_DIV2, }; ---8--- That's the same code I'm looking at. This has always worked as expected for me in the linux-3.10-at91 kernel. However, in the linux-3.18-at91 kernel, I definitely recall getting half the rate when querying the ssc0 clock. I thought the 3.18 way was the future, and adjusted. However, I don't get that difference now, and I can't really explain what has changed. Strange. Anyway, thanks for setting me straight! That means, the clock output from PMC is system clock / 2 which will be fed to ssc (here we call it SSC peripheral clock = system clock / 2). been changed to mean the rate after the fixed divider by two that is activated as soon as the ssc clock divider (given by SSC_CMR) is activated, and that it was a simple matter of multiplying by two to get to the MCK rate. I further assumed that Master Clock in the Serial Clock Ratio Considerations section was this MCK. Maybe the mistake was to involve the peripheral clock at all? Ok, so I may have misunderstood, but in that case what does that mean in terms of finding the Master Clock rate that is mentioned in the Serial Clock Ratio Considerations section? Is it perhaps the rate of the parent clock of the given ssc clk? Or, given the above explanation, is it correct to simply multiply by two as I have done? The Master Clock actually is the same as Peripheral clock. Thanks for your information, I will send this to our datasheet team to update this part. You are still not saying how to get to the Master Clock rate (i.e. the Master Clock that is mentioned in the Serial Clock Ratio Considerations section. You are only implying that multiplying the ssc clock rate with 2 is wrong for some reason. I mean in that section you mentioned. The Master Clock is the same as Peripheral Clock. So, you get the SSC peripheral clock is what the clock (Master Clock) you try to get ( clk_get_rate(ssc_p-ssc-clk)). Yes, I see now. I'll resend with the *2 (and the 500ppm discussed in the other thread) removed. Are you
RE: [PATCH] ASoC:pcm512x: Fix divide by zero issue.
Howard Mitchell wrote; If den=1 and pllin_rate20MHz then den and num are adjusted to 0 causing a divide by zero error a few lines further on. Therefore this patch correctly scales num and den such that pllin_rate/den 20MHz as required in the device data sheet. Yep, the old code is plain broken and never actually ran, we have been using 16MHz pllin_rate, and I apparently never hit this code path. Signed-off-by: Howard Mitchell h...@hmbedded.co.uk Acked-by: Peter Rosin p...@axentia.se Cheers, Peter --- sound/soc/codecs/pcm512x.c |4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 5301c4a..8472099 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -713,8 +713,8 @@ static int pcm512x_find_pll_coeff(struct snd_soc_dai *dai, /* pllin_rate / P (or here, den) cannot be greater than 20 MHz */ if (pllin_rate / den 2000 num 8) { - num *= 2000 / (pllin_rate / den); - den *= 2000 / (pllin_rate / den); + num *= DIV_ROUND_UP(pllin_rate / den, 2000); + den *= DIV_ROUND_UP(pllin_rate / den, 2000); } dev_dbg(dev, num / den = %lu / %lu\n, num, den); -- 1.7.9.5 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH] ASoC:pcm512x: Make PLL lock output selectable via device tree.
Howard Mitchell wrote: Currently the PLL Lock output signal is hardcoded to GPIO4. This makes it seletable in the same way as pll-in and pll-out. Oops, I never intended the plllock code the hit upstream. I thought I had removed that testing code and was very surprised to see it, that was an odd experience. From my point of view it is fine to instead remove the whole pll-lock thing. But now the cat is out, so maybe we have to keep a way to output the pll-lock signal for backwards compatibility? Appart from the space-indent changes, this looks fine (if we do in fact need to keep it at all). But I would like to see a new version of the patch without the whitespace changes before I commit to that. Cheers, Peter Signed-off-by: Howard Mitchell h...@hmbedded.co.uk --- .../devicetree/bindings/sound/pcm512x.txt |3 ++ sound/soc/codecs/pcm512x.c | 47 +--- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/Documentation/devicetree/bindings/sound/pcm512x.txt b/Documentation/devicetree/bindings/sound/pcm512x.txt index 3aae3b4..432f186 100644 --- a/Documentation/devicetree/bindings/sound/pcm512x.txt +++ b/Documentation/devicetree/bindings/sound/pcm512x.txt @@ -26,6 +26,8 @@ Optional properties: given pll-in pin and PLL output on the given pll-out pin. An external connection from the pll-out pin to the SCLK pin is assumed. + - pll-lock : gpio pin used to output the PLL lock flag. + Examples: pcm5122: pcm5122@4c { @@ -49,4 +51,5 @@ Examples: clocks = sck; pll-in = 3; pll-out = 6; + pll-lock = 4; }; diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index 8472099..a4217d7 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -49,6 +49,7 @@ struct pcm512x_priv { int fmt; int pll_in; int pll_out; + int pll_lock; int pll_r; int pll_j; int pll_d; @@ -1296,24 +1297,26 @@ static int pcm512x_hw_params(struct snd_pcm_substream *substream, ret, pcm512x-pll_out); return ret; } + } - gpio = PCM512x_G1OE (4 - 1); - ret = regmap_update_bits(pcm512x-regmap, PCM512x_GPIO_EN, - gpio, gpio); - if (ret != 0) { - dev_err(codec-dev, Failed to enable gpio %d: %d\n, - 4, ret); - return ret; - } - - gpio = PCM512x_GPIO_OUTPUT_1 + 4 - 1; - ret = regmap_update_bits(pcm512x-regmap, gpio, - PCM512x_GxSL, PCM512x_GxSL_PLLLK); - if (ret != 0) { - dev_err(codec-dev, - Failed to output pll lock on %d: %d\n, - ret, 4); - return ret; + if (pcm512x-pll_lock) { +gpio = PCM512x_G1OE (pcm512x-pll_lock - 1); +ret = regmap_update_bits(pcm512x-regmap, PCM512x_GPIO_EN, + gpio, gpio); +if (ret != 0) { +dev_err(codec-dev, Failed to enable gpio %d: %d\n, +pcm512x-pll_lock, ret); +return ret; +} + +gpio = PCM512x_GPIO_OUTPUT_1 + pcm512x-pll_lock - 1; +ret = regmap_update_bits(pcm512x-regmap, gpio, + PCM512x_GxSL, PCM512x_GxSL_PLLLK); +if (ret != 0) { +dev_err(codec-dev, +Failed to output pll lock on %d: %d\n, +ret, pcm512x-pll_lock); +return ret; } } @@ -1518,6 +1521,16 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) ret = -EINVAL; goto err_clk; } + +if (of_property_read_u32(np, pll-lock, val) = 0) { +if (val 6) { +dev_err(dev, Invalid pll-lock\n); +ret = -EINVAL; +goto err_clk; +} +pcm512x-pll_lock = val; +} + } #endif -- 1.7.9.5 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH] ASoC:pcm512x: Make PLL lock output selectable via device tree.
Howard Mitchell wrote: On 22/03/15 16:24, Mark Brown wrote: On Fri, Mar 20, 2015 at 09:22:43PM +, Howard Mitchell wrote: + if (pcm512x-pll_lock) { +if (of_property_read_u32(np, pll-lock, val) = 0) { +if (val 6) { +dev_err(dev, Invalid pll-lock\n); +ret = -EINVAL; +goto err_clk; +} +pcm512x-pll_lock = val; +} This breaks existing boards which rely on GPIO 4 being set as the lock output. This is very unfortunate since it's a silly thing for the driver to default to but nontheless we should really continue to support them - at a guess Peter's board is relying on this, and even if it isn't someone else's might. I take your point, but the reason I pushed this patch was that I wanted to use GPIO4 for pll-out and unfortunately because the pll-lock configuration is after the pll-out configuration it stomps on it. If I modify the patch to provide a default for pll-lock I will then be obliged to specify pll-lock on another GPIO. The pcm5122 has limited IO so being forced to have a GPIO for pll-lock seems wrong to me. A future user of the device may well decide to use the GPIOs for other purposes and therefore not want a pll-lock signal at all. Surely we should allow for that possibility? Given that Peter has indicated that he'd be happy with this solution and that this code hasn't reached a published kernel would it be reasonable to go ahead with my current patch (happy to clean up the indent issues that Peter pointed out of course)? Strongly agreed that we should fix this before it is published (I assumed that is was included in 3.19, it felt so long ago that Mark merged it...). My preference would be to remove the pll-lock things entirely though. Assuming you don't need it for your board of course, but I doubt it from your description. I used it to make sure I had understood the chip correctly, that's all. Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v2] ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP
From: Peter Rosin p...@axentia.se When using non-standard rates, a relatively small amount of overclocking can make a big difference to a number of cases. - Not all rates are possible to achieve with the PLL, due to divider restrictions. - The higher oversampling rates that can be used by the DAC, the simpler the analog output filters get (mirror frequencies move up, away from the desired spectrum). - The more work the DSP can perform per sample, the better. For standard rates, there is little to gain as everything is designed just right, and the needed overclocking to make a real difference would be significant. Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/codecs/pcm512x.c | 161 +--- 1 file changed, 150 insertions(+), 11 deletions(-) diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index f11c76f1acfe..4b5f1fe9be97 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -54,6 +54,9 @@ struct pcm512x_priv { int pll_d; int pll_p; unsigned long real_pll; + unsigned long overclock_pll; + unsigned long overclock_dac; + unsigned long overclock_dsp; }; /* @@ -224,6 +227,90 @@ static bool pcm512x_volatile(struct device *dev, unsigned int reg) } } +static int pcm512x_overclock_pll_get(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol-value.integer.value[0] = pcm512x-overclock_pll; + return 0; +} + +static int pcm512x_overclock_pll_put(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (codec-dapm.bias_level) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x-overclock_pll = ucontrol-value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dsp_get(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol-value.integer.value[0] = pcm512x-overclock_dsp; + return 0; +} + +static int pcm512x_overclock_dsp_put(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (codec-dapm.bias_level) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x-overclock_dsp = ucontrol-value.integer.value[0]; + return 0; +} + +static int pcm512x_overclock_dac_get(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + ucontrol-value.integer.value[0] = pcm512x-overclock_dac; + return 0; +} + +static int pcm512x_overclock_dac_put(struct snd_kcontrol *kcontrol, +struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_codec_get_drvdata(codec); + + switch (codec-dapm.bias_level) { + case SND_SOC_BIAS_OFF: + case SND_SOC_BIAS_STANDBY: + break; + default: + return -EBUSY; + } + + pcm512x-overclock_dac = ucontrol-value.integer.value[0]; + return 0; +} + static const DECLARE_TLV_DB_SCALE(digital_tlv, -10350, 50, 1); static const DECLARE_TLV_DB_SCALE(analog_tlv, -600, 600, 0); static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 80, 0); @@ -328,6 +415,13 @@ SOC_ENUM(Volume Ramp Up Rate, pcm512x_vnuf), SOC_ENUM(Volume Ramp Up Step, pcm512x_vnus), SOC_ENUM(Volume Ramp Down Emergency Rate, pcm512x_vedf), SOC_ENUM(Volume Ramp Down Emergency Step, pcm512x_veds), + +SOC_SINGLE_EXT(Max Overclock PLL, SND_SOC_NOPM, 0, 20, 0, + pcm512x_overclock_pll_get, pcm512x_overclock_pll_put), +SOC_SINGLE_EXT(Max Overclock DSP, SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dsp_get, pcm512x_overclock_dsp_put), +SOC_SINGLE_EXT(Max Overclock DAC, SND_SOC_NOPM, 0, 40, 0, + pcm512x_overclock_dac_get, pcm512x_overclock_dac_put), }; static const
Re: [PATCH 2/2] ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP
From: Peter Rosin p...@axentia.se On 2015-02-23 15:31, Mark Brown wrote: On Sun, Feb 22, 2015 at 12:24:12AM +, Peter Rosin wrote: ...but I'm not sure everybody agrees that overclocking games should be allowed by any and all users? I don't see why not, ASoC controls are already way beyond end user a lot of the time. *snip* Ok, so going to ALSA-control route, changes since v1: - Uses ALSA-controls instead of sysfs knobs. - Returns -EBUSY if trying to change the values while active. Cheers, Peter Peter Rosin (1): ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP sound/soc/codecs/pcm512x.c | 161 +--- 1 file changed, 150 insertions(+), 11 deletions(-) -- 1.7.10.4 -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH 2/2] ASoC: pcm512x: Allow independently overclocking PLL, DAC and DSP
Mark Brown wrote: On Mon, Feb 16, 2015 at 10:02:48PM +0100, Peter Rosin wrote: From: Peter Rosin p...@axentia.se When using non-standard rates, a relatively small amount of overclocking can make a big difference to a number of cases. This is all basically fine but I'm wondering why this is being configured via sysfs and not via ALSA controls? It's going to be more fiddly for people to have to work with both control methods when they need to configure these things. Originally, I had the limits in .config. Then Lars-Peter suggested sysfs (on irc) and perhaps some way to disable overclocking. ALSA controls were never on the table, but now that you mention it, it sounds about right. So, I'm fine with having it as ALSA-controls... *time passes* ...but I'm not sure everybody agrees that overclocking games should be allowed by any and all users? If you still want me to convert to ALSA controls, what control do you suggest? SOC_SINGLE_EXT? Or should I use an enumeration, because mixers tend to present volume controls as a percentage of max, which will be confusing: You are now at volume 75% (of max 40), when the value is 30. Eeek. But enumerations from 0% to 40% sounds tedious. And how would you suggest that I name the controls? Max Overclock DAC, Max Overclock DSP and Max Overclock PLL? BTW, the only troubles I've had with overclocking too much is that it has stopped working. I have not managed to fry any chip. But that is no guarantee, of course. Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH] ASoC: pcm512x: Remove hardcoding of pll-lock to GPIO4
Howard Mitchell wrote: Currently GPIO4 is hardcoded to output the pll-lock signal. Unfortunately this is after the pll-out GPIO is configured which is selectable in the device tree. Therefore it is not possible to use GPIO4 for pll-out. Therefore this patch removes the configuration of GPIO4. Howard, thanks for picking up my laundry! Is master mode working for you otherwise? Have you seen any sign of bad dividers for the various clocks, or anything like that? Regarding the pin configuration, I suppose the cleanest approach would be to implement it as a pin control driver? Then you could also expose the pins as gpios and select the pin functions in a more standard way, right? I wouldn't know where to start with that though. I have the feeling that it would also mean that there would have to be a mfd driver for the chip??? However, there is the issue that we don't need anything more from the chip, so we're happy with the driver as is (assuming the overclocking patch goes in as planned for 4.1). Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[RFC PATCH] ASoC: atmel_ssc_dai: Allow more rates
From: Peter Rosin p...@axentia.se When the SSC acts as BCK master, use a ratnum rule to limit the rate instead of only doing the standard rates. When the SSC acts as BCK slave, allow any BCK frequency up to within 500ppm of the SSC master clock, possibly divided by 2, 3 or 6. Put a cap at 384kHz. Who's /ever/ going to need more than that? Signed-off-by: Peter Rosin p...@axentia.se --- sound/soc/atmel/atmel_ssc_dai.c | 98 +-- sound/soc/atmel/atmel_ssc_dai.h |1 + 2 files changed, 95 insertions(+), 4 deletions(-) This patch depends on the patch I sent earlier that enables the _CBS_CFS mode on the atmel SSC dai (1). I'm unsure about the rate rules for reception on the TK pin when outputting LRCLK and the rate rules for transmitting on the RK pin when inputting LRCLK. The datasheet for my chip (SAMA5D31) doesn't say anything about what rates are allowed when using the RK pin for transmitting or the TK pin for receiving. Another question I have is if these rate rules are the same across all chips supported by this driver, or if there is need for some amount of special casing? And what special casing if that is the case? The reason I'm digging around in this area is that I need support for inputting a BCK of 16MHz on the TK pin during playback, i.e. with the codec mastering BCK. And the only way I can get that is to use _CBM_CFS. My SSC master clock is 66MHz, which means that the max supported rate for _CBM_CFM is 11MHz, i.e. not enough. This patch unlocks my use case, but I would like to correctly stop rates that the SSC does not support in other use cases as well. I simply do not have the hardware to fully test this myself. An lastly, is it reasonable to deduct 500ppm to allow for some clock skew? FYI, the 500ppm was pulled out of my hat... Cheers, Peter (1) ASoC: atmel_ssc_dai: Support SND_SOC_DAIFMT_CBM_CFS on I2S https://lkml.org/lkml/2015/1/29/373 diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index f55f3aab8bdd..11eab65c86c8 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -187,6 +187,76 @@ static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct atmel_ssc_info *ssc_p = rule-private; + struct ssc_device *ssc = ssc_p-ssc; + struct snd_interval *i = hw_param_interval(params, rule-var); + struct snd_interval t; + struct snd_ratnum r = { + .den_min = 1, + .den_max = 4095, + .den_step = 1, + }; + unsigned int num = 0, den = 0; + int frame_size; + int mck_div = 2; + int ret; + + frame_size = snd_soc_params_to_frame_size(params); + if (frame_size 0) + return frame_size; + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFS: + if ((ssc_p-dir_mask SSC_DIR_MASK_CAPTURE) +ssc-clk_from_rk_pin) + /* Receiver Frame Synchro is output on RK pin. +* What rules are there when outputting on the TK pin? +*/ + mck_div = 3; + break; + + case SND_SOC_DAIFMT_CBM_CFM: + if ((ssc_p-dir_mask SSC_DIR_MASK_PLAYBACK) +!ssc-clk_from_rk_pin) + /* Transmit Frame Synchro is input on TK pin. +* What rules are there when inputting from the RK pin? +*/ + mck_div = 6; + break; + } + + switch (ssc_p-daifmt SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + r.num = ssc_p-mck_rate / mck_div / frame_size; + + ret = snd_interval_ratnum(i, 1, r, num, den); + if (ret = 0 den rule-var == SNDRV_PCM_HW_PARAM_RATE) { + params-rate_num = num; + params-rate_den = den; + } + break; + + case SND_SOC_DAIFMT_CBM_CFS: + case SND_SOC_DAIFMT_CBM_CFM: + t.min = 8000; + t.max = ssc_p-mck_rate / mck_div / frame_size; + /* Take away 500ppm, just to be on the safe side. */ + t.max -= t.max / 2000; + t.openmin = t.openmax = 0; + t.integer = 0; + ret = snd_interval_refine(i, t); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} /*-*\ * DAI functions @@ -200,10 +270,17 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream, struct atmel_ssc_info *ssc_p = ssc_info[dai-id
Re: [PATCH] adm8211: fix checkpatch errors for indentation and new line around switch-case
diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index f07a618..058fb4b 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1098,14 +1098,18 @@ static void adm8211_hw_init(struct ieee80211_hw *dev) pci_read_config_byte(priv-pdev, PCI_CACHE_LINE_SIZE, cline); switch (cline) { - case 0x8: reg |= (0x1 14); -break; - case 0x16: reg |= (0x2 14); -break; - case 0x32: reg |= (0x3 14); -break; - default: reg |= (0x0 14); -break; + case 0x8: + reg |= (0x1 14); + break; + case 0x16: + reg |= (0x2 14); + break; + case 0x32: + reg |= (0x3 14); + break; + default: + reg |= (0x0 14); + break; } } Those 0x16/0x32 hexadecimal case-selectors looking suspiciously like decimal bits need a comment if they are in fact correct, which I doubt. Cheers, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
RE: [PATCH] ASoC: tfa9879: Fix return value check in tfa9879_i2c_probe()
Wei Yongjun wrote: In case of error, the function devm_kzalloc() returns NULL not ERR_PTR(). The IS_ERR() test in the return value check should be replaced with NULL test. Acked-by: Peter Rosin p...@axentia.se Thanks, Peter -- To unsubscribe from this list: send the line unsubscribe linux-kernel in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-19 10:51, Ludovic Desroches wrote: > Hi Peter, > > On Fri, Oct 16, 2015 at 11:08:42AM +0200, Peter Rosin wrote: >> On 2015-10-16 01:47, Peter Rosin wrote: >>> On 2015-10-14 07:43, Ludovic Desroches wrote: >>>> On Tue, Oct 13, 2015 at 08:01:34PM +0200, Peter Rosin wrote: >>>>> On 2015-10-13 18:47, Cyrille Pitchen wrote: >>>>>> Le 13/10/2015 17:19, Peter Rosin a écrit : >>>>>>> On 2015-10-13 16:21, Ludovic Desroches wrote: > > [...] > >>> I have started to get this when I run with this patch: >>> >>> [ 73.31] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 198.20] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 509.88] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 615.75] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 617.75] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 1766.64] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 2035.38] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 2227.19] at91_i2c f0014000.i2c: RXRDY still set! >>> [ 2313.10] at91_i2c f0014000.i2c: RXRDY still set! >>> >>> My USB serial dongle was hung which was why I didn't notice until just now. >>> >>> This is probably not when communication with the eeprom though, and >>> certainly not >>> writing to it, but perhpaps when polling the temperature (using the jc42 >>> driver). >>> I'll investigate further in the morning to see if I can pinpoint it. >> >> Yep, it's the jc42 accesses that triggers this (to the same chip as the >> eeprom, but a different block of transistors I suppose). >> >> Looking at the i2c bus, the accesses normally go like this: >> >> [0.00] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >> [0.000521] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >> [0.001024] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >> [0.001524] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >> [0.196991] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >> [0.197514] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >> [0.198019] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >> [0.198520] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >> >> I.e. chunks of four transfers every ~200 ms (I removed the 1Hz rate >> limiter in the jc42 driver to get more frequent incidents). >> >> But when the diagnostic (RXRDY still set!) is output it continues >> with this: >> >> [0.399755] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >> [0.404998] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >> [0.405508] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >> [0.406008] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >> >> Notice the ~5 ms delay (about the time it should take to output >> the diagnostic at 115200 baud) after the access to register 0x05 >> at 0.399755. >> >> This is the only thing that I can observe on the bus, so it appears >> to be harmless. >> >> It appears that the i2c access at 0.399755 finds the TWI >> registers in an odd state, but nothing from the access at >> 0.198520 appears to have gone wrong. Is this a race? Anyway, >> the diagnostic is pretty frequent and annoying. printk_once? > > I'll try to reproduce it on my side. The only issue you have is having > the message about RXRDY? I mean no issue with i2c transfers? Exactly, the only issue is the message, the bus looks good and transfers work as they should AFAICT. > It is not the possible bug we had in mind, this bug will prevent the > master device to release the i2c bus. It will stop the transfer but > without sending a stop on the bus. Agreed, this is not about the extra frame caused by the spurious write to the THR register. This is something else. One suspicion is that the driver gets an unexpected irq from its own NACK (the one that it puts out to end the read) and races with the expected interrupt at TXCOMP? Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-14 07:43, Ludovic Desroches wrote: > On Tue, Oct 13, 2015 at 08:01:34PM +0200, Peter Rosin wrote: >> On 2015-10-13 18:47, Cyrille Pitchen wrote: >>> Le 13/10/2015 17:19, Peter Rosin a écrit : >>>> On 2015-10-13 16:21, Ludovic Desroches wrote: >>>>> From: Cyrille Pitchen <cyrille.pitc...@atmel.com> >>>>> >>>>> In some cases a NACK interrupt may be pending in the Status Register (SR) >>>>> as a result of a previous transfer. However at91_do_twi_transfer() did not >>>>> read the SR to clear pending interruptions before starting a new transfer. >>>>> Hence a NACK interrupt rose as soon as it was enabled again at the I2C >>>>> controller level, resulting in a wrong sequence of operations and strange >>>>> patterns of behaviour on the I2C bus, such as a clock stretch followed by >>>>> a restart of the transfer. >>>>> >>>>> This first issue occurred with both DMA and PIO write transfers. >>>>> >>>>> Also when a NACK error was detected during a PIO write transfer, the >>>>> interrupt handler used to wrongly start a new transfer by writing into the >>>>> Transmit Holding Register (THR). Then the I2C slave was likely to reply >>>>> with a second NACK. >>>>> >>>>> This second issue is fixed in atmel_twi_interrupt() by handling the TXRDY >>>>> status bit only if both the TXCOMP and NACK status bits are cleared. >>>>> >>>>> Tested with a at24 eeprom on sama5d36ek board running a linux-4.1-at91 >>>>> kernel image. Adapted to linux-next. >>>>> >>>>> Signed-off-by: Cyrille Pitchen <cyrille.pitc...@atmel.com> >>>>> Fixes: 93563a6a71bb ("i2c: at91: fix a race condition when using the DMA >>>>> controller") >>>>> Reported-by: Peter Rosin <p...@lysator.liu.se> >>>>> Signed-off-by: Ludovic Desroches <ludovic.desroc...@atmel.com> >>>>> Cc: sta...@vger.kernel.org #4.1 >>>> >>>> The regression is gone with this patch (vanilla 4.2), thank you! >>>> >>>> However, looking at the bus, there are two NACKs after each >>>> successful chunk of memory written by the eeprom driver. >>>> >>>> Looking at the eeprom driver, I expect this on the bus: >>>> >>>> S 0x50 0x00 "hello there guys" P >>>> S 0x50 NACK P >>>> delay for at least 1 ms (since the eeprom driver has a msleep(1) call). >>>> S 0x50 NACK P >>>> delay for at least 1 ms >>>> ... >>>> ... >>>> S 0x50 NACK P >>>> delay for at least 1 ms >>>> S 0x50 0x10 "and girls\n" P >>>> >>>> This is not what I observe on the bus, most of the time there is a >>>> second NACK immediately following the first. I suspect that it is >>>> the i2c bus driver that somehow confuses itself and reissues the >>>> command for some reason? >>>> >>>> But this behavior has been there since the beginning, so it's probably >>>> orthogonal, and killing the data corrupting regression is much more >>>> important to me than fussing over a surplus failed transfer. Hence >>>> >>>> Tested-by: Peter Rosin <p...@lysator.liu.se> >>>> >>>> Cheers, >>>> Peter >>>> >>> >>> Hi Peter, >>> >>> sama5d3x and sama5d4x don't support the so called "Alternative Command mode" >>> whereas sama5d2x do. The Alternative Command mode comes with a new hardware >>> mechanism inside the I2C controller which locks the transmission of data on >>> the I2C bus when a NACK is detected. It means that even if a data is written >>> into the THR, the I2C controller doesn't push this data on the I2C bus but >>> retains the data in the THR (and its associated FIFO for sama5d2x and future >>> SoCs) until the driver unlocks the transmitter by writing the LOCKCLR (Lock >>> Clear) bit in the Control Register. Then and only then, the transmitter >>> outputs >>> pending data again. >>> This new mechanism was introduced to cope with an unwanted DMA write into >>> the >>> THR after a NACK. Indeed, as I've tried to explain in my patch, when a first >>> NACK is detected, the I2C controller sets the TXCOMP, NACK and TXRDY bits >>> alltogether in the Status Register. However s
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-16 01:47, Peter Rosin wrote: > On 2015-10-14 07:43, Ludovic Desroches wrote: >> On Tue, Oct 13, 2015 at 08:01:34PM +0200, Peter Rosin wrote: >>> On 2015-10-13 18:47, Cyrille Pitchen wrote: >>>> Le 13/10/2015 17:19, Peter Rosin a écrit : >>>>> On 2015-10-13 16:21, Ludovic Desroches wrote: >>>>>> From: Cyrille Pitchen <cyrille.pitc...@atmel.com> >>>>>> >>>>>> In some cases a NACK interrupt may be pending in the Status Register (SR) >>>>>> as a result of a previous transfer. However at91_do_twi_transfer() did >>>>>> not >>>>>> read the SR to clear pending interruptions before starting a new >>>>>> transfer. >>>>>> Hence a NACK interrupt rose as soon as it was enabled again at the I2C >>>>>> controller level, resulting in a wrong sequence of operations and strange >>>>>> patterns of behaviour on the I2C bus, such as a clock stretch followed by >>>>>> a restart of the transfer. >>>>>> >>>>>> This first issue occurred with both DMA and PIO write transfers. >>>>>> >>>>>> Also when a NACK error was detected during a PIO write transfer, the >>>>>> interrupt handler used to wrongly start a new transfer by writing into >>>>>> the >>>>>> Transmit Holding Register (THR). Then the I2C slave was likely to reply >>>>>> with a second NACK. >>>>>> >>>>>> This second issue is fixed in atmel_twi_interrupt() by handling the TXRDY >>>>>> status bit only if both the TXCOMP and NACK status bits are cleared. >>>>>> >>>>>> Tested with a at24 eeprom on sama5d36ek board running a linux-4.1-at91 >>>>>> kernel image. Adapted to linux-next. >>>>>> >>>>>> Signed-off-by: Cyrille Pitchen <cyrille.pitc...@atmel.com> >>>>>> Fixes: 93563a6a71bb ("i2c: at91: fix a race condition when using the DMA >>>>>> controller") >>>>>> Reported-by: Peter Rosin <p...@lysator.liu.se> >>>>>> Signed-off-by: Ludovic Desroches <ludovic.desroc...@atmel.com> >>>>>> Cc: sta...@vger.kernel.org #4.1 >>>>> >>>>> The regression is gone with this patch (vanilla 4.2), thank you! >>>>> >>>>> However, looking at the bus, there are two NACKs after each >>>>> successful chunk of memory written by the eeprom driver. >>>>> >>>>> Looking at the eeprom driver, I expect this on the bus: >>>>> >>>>> S 0x50 0x00 "hello there guys" P >>>>> S 0x50 NACK P >>>>> delay for at least 1 ms (since the eeprom driver has a msleep(1) call). >>>>> S 0x50 NACK P >>>>> delay for at least 1 ms >>>>> ... >>>>> ... >>>>> S 0x50 NACK P >>>>> delay for at least 1 ms >>>>> S 0x50 0x10 "and girls\n" P >>>>> >>>>> This is not what I observe on the bus, most of the time there is a >>>>> second NACK immediately following the first. I suspect that it is >>>>> the i2c bus driver that somehow confuses itself and reissues the >>>>> command for some reason? >>>>> >>>>> But this behavior has been there since the beginning, so it's probably >>>>> orthogonal, and killing the data corrupting regression is much more >>>>> important to me than fussing over a surplus failed transfer. Hence >>>>> >>>>> Tested-by: Peter Rosin <p...@lysator.liu.se> >>>>> >>>>> Cheers, >>>>> Peter >>>>> >>>> >>>> Hi Peter, >>>> >>>> sama5d3x and sama5d4x don't support the so called "Alternative Command >>>> mode" >>>> whereas sama5d2x do. The Alternative Command mode comes with a new hardware >>>> mechanism inside the I2C controller which locks the transmission of data on >>>> the I2C bus when a NACK is detected. It means that even if a data is >>>> written >>>> into the THR, the I2C controller doesn't push this data on the I2C bus but >>>> retains the data in the THR (and its associated FIFO for sama5d2x and >>>> future >>>> SoCs) until the driver unlocks the transmitter by writing the LOCKCLR (Loc
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-20 15:27, Ludovic Desroches wrote: > On Mon, Oct 19, 2015 at 12:49:03PM +0200, Peter Rosin wrote: >> On 2015-10-19 10:51, Ludovic Desroches wrote: >>> Hi Peter, >>> >>> On Fri, Oct 16, 2015 at 11:08:42AM +0200, Peter Rosin wrote: >>>> On 2015-10-16 01:47, Peter Rosin wrote: >>>>> On 2015-10-14 07:43, Ludovic Desroches wrote: >>>>>> On Tue, Oct 13, 2015 at 08:01:34PM +0200, Peter Rosin wrote: >>>>>>> On 2015-10-13 18:47, Cyrille Pitchen wrote: >>>>>>>> Le 13/10/2015 17:19, Peter Rosin a écrit : >>>>>>>>> On 2015-10-13 16:21, Ludovic Desroches wrote: >>> >>> [...] >>> >>>>> I have started to get this when I run with this patch: >>>>> >>>>> [ 73.31] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 198.20] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 509.88] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 615.75] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 617.75] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 1766.64] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 2035.38] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 2227.19] at91_i2c f0014000.i2c: RXRDY still set! >>>>> [ 2313.10] at91_i2c f0014000.i2c: RXRDY still set! >>>>> >>>>> My USB serial dongle was hung which was why I didn't notice until just >>>>> now. >>>>> >>>>> This is probably not when communication with the eeprom though, and >>>>> certainly not >>>>> writing to it, but perhpaps when polling the temperature (using the jc42 >>>>> driver). >>>>> I'll investigate further in the morning to see if I can pinpoint it. >>>> >>>> Yep, it's the jc42 accesses that triggers this (to the same chip as the >>>> eeprom, but a different block of transistors I suppose). >>>> >>>> Looking at the i2c bus, the accesses normally go like this: >>>> >>>> [0.00] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >>>> [0.000521] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >>>> [0.001024] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >>>> [0.001524] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >>>> [0.196991] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >>>> [0.197514] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >>>> [0.198019] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >>>> [0.198520] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >>>> >>>> I.e. chunks of four transfers every ~200 ms (I removed the 1Hz rate >>>> limiter in the jc42 driver to get more frequent incidents). >>>> >>>> But when the diagnostic (RXRDY still set!) is output it continues >>>> with this: >>>> >>>> [0.399755] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >>>> [0.404998] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >>>> [0.405508] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >>>> [0.406008] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >>>> >>>> Notice the ~5 ms delay (about the time it should take to output >>>> the diagnostic at 115200 baud) after the access to register 0x05 >>>> at 0.399755. >>>> >>>> This is the only thing that I can observe on the bus, so it appears >>>> to be harmless. >>>> >>>> It appears that the i2c access at 0.399755 finds the TWI >>>> registers in an odd state, but nothing from the access at >>>> 0.198520 appears to have gone wrong. Is this a race? Anyway, >>>> the diagnostic is pretty frequent and annoying. printk_once? >>> >>> I'll try to reproduce it on my side. The only issue you have is having >>> the message about RXRDY? I mean no issue with i2c transfers? >> >> Exactly, the only issue is the message, the bus looks good and transfers >> work as they should AFAICT. >>> It is not the possible bug we had in mind, this bug will prevent the >>> master device to release the i2c bus. It will stop the transfer but >>> without sending a stop on the bus. >> >> Agreed, this is not about the extra frame caused by the spurious write >> to the THR register. This is something else. >> >> One suspicion is that the driver gets an unexpected irq from its own >> NACK (the one that it puts out to end the read) and races with the >> expected interrupt a
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-21 09:21, Peter Rosin wrote: > On 2015-10-20 15:27, Ludovic Desroches wrote: >> On Mon, Oct 19, 2015 at 12:49:03PM +0200, Peter Rosin wrote: >>> On 2015-10-19 10:51, Ludovic Desroches wrote: >>>> Hi Peter, >>>> >>>> On Fri, Oct 16, 2015 at 11:08:42AM +0200, Peter Rosin wrote: >>>>> On 2015-10-16 01:47, Peter Rosin wrote: >>>>>> On 2015-10-14 07:43, Ludovic Desroches wrote: >>>>>>> On Tue, Oct 13, 2015 at 08:01:34PM +0200, Peter Rosin wrote: >>>>>>>> On 2015-10-13 18:47, Cyrille Pitchen wrote: >>>>>>>>> Le 13/10/2015 17:19, Peter Rosin a écrit : >>>>>>>>>> On 2015-10-13 16:21, Ludovic Desroches wrote: >>>> >>>> [...] >>>> >>>>>> I have started to get this when I run with this patch: >>>>>> >>>>>> [ 73.31] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 198.20] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 509.88] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 615.75] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 617.75] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 1766.64] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 2035.38] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 2227.19] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> [ 2313.10] at91_i2c f0014000.i2c: RXRDY still set! >>>>>> >>>>>> My USB serial dongle was hung which was why I didn't notice until just >>>>>> now. >>>>>> >>>>>> This is probably not when communication with the eeprom though, and >>>>>> certainly not >>>>>> writing to it, but perhpaps when polling the temperature (using the jc42 >>>>>> driver). >>>>>> I'll investigate further in the morning to see if I can pinpoint it. >>>>> >>>>> Yep, it's the jc42 accesses that triggers this (to the same chip as the >>>>> eeprom, but a different block of transistors I suppose). >>>>> >>>>> Looking at the i2c bus, the accesses normally go like this: >>>>> >>>>> [0.00] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >>>>> [0.000521] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >>>>> [0.001024] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >>>>> [0.001524] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >>>>> [0.196991] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >>>>> [0.197514] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >>>>> [0.198019] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >>>>> [0.198520] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >>>>> >>>>> I.e. chunks of four transfers every ~200 ms (I removed the 1Hz rate >>>>> limiter in the jc42 driver to get more frequent incidents). >>>>> >>>>> But when the diagnostic (RXRDY still set!) is output it continues >>>>> with this: >>>>> >>>>> [0.399755] S 0x18 W 0x05 S 0x18 R 0xc1 0xbe NACK P >>>>> [0.404998] S 0x18 W 0x04 S 0x18 R 0x00 0x00 NACK P >>>>> [0.405508] S 0x18 W 0x03 S 0x18 R 0x00 0x00 NACK P >>>>> [0.406008] S 0x18 W 0x02 S 0x18 R 0x00 0x00 NACK P >>>>> >>>>> Notice the ~5 ms delay (about the time it should take to output >>>>> the diagnostic at 115200 baud) after the access to register 0x05 >>>>> at 0.399755. >>>>> >>>>> This is the only thing that I can observe on the bus, so it appears >>>>> to be harmless. >>>>> >>>>> It appears that the i2c access at 0.399755 finds the TWI >>>>> registers in an odd state, but nothing from the access at >>>>> 0.198520 appears to have gone wrong. Is this a race? Anyway, >>>>> the diagnostic is pretty frequent and annoying. printk_once? >>>> >>>> I'll try to reproduce it on my side. The only issue you have is having >>>> the message about RXRDY? I mean no issue with i2c transfers? >>> >>> Exactly, the only issue is the message, the bus looks good and transfers >>> work as they should AFAICT. >>>> It is not the possible bug we had in mind, this bug will prevent the >>>> master device to release the i2c bus. It will stop the transfer but >>>> without
Re: Regression: at24 eeprom writing
On 2015-10-05 17:09, Peter Rosin wrote: > But what trouble does the i2c bus driver see? Admittedly I only > have a simple logic level bus viewer, and not a full-blown > oscilloscope, so there might be something analogue going on? > I don't think so though, those signals looked fine last time we > looked (but we obviously didn't have these issues then, and > didn't really look that closely). I'll see if I can recheck > with a real scope too. We redid the tests with a real scope, and the signal looks nice and square, so it is not that. Speculating further on the cause of the long ACKs, I think that the i2c driver gets confused by an interrupt that marks the transfer complete, and thinks it's a NACK interrupt instead. Is that plausible? If the peripheral unit is such that it generates a stop automatically on NACKs, then this makes perfect sense. I.e. the TWI sees that the transfer is complete, generates an interrupt, and waits for further data or a stop command. Meanwhile the driver thinks it's a NACK and that a stop condition has already been sent to the bus, and just notifies the i2c consumer (the eeprom driver in this case) of the failure and frees up the bus for any future user. This also matches what I see when I turn on some more traffic on the bus, that is interleaved with the eeprom traffic. AFAICT, it can be any command that gets chewed up by the eeprom if it is sent to the i2c driver during the long ACK. Are you Atmel people making any progress on this data corrupting regression? Is there anything else I can do to help? Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: Regression: at24 eeprom writing
On 2015-10-12 18:13, Cyrille Pitchen wrote: > Le 12/10/2015 17:13, Peter Rosin a écrit : >> On 2015-10-05 17:09, Peter Rosin wrote: >>> But what trouble does the i2c bus driver see? Admittedly I only >>> have a simple logic level bus viewer, and not a full-blown >>> oscilloscope, so there might be something analogue going on? >>> I don't think so though, those signals looked fine last time we >>> looked (but we obviously didn't have these issues then, and >>> didn't really look that closely). I'll see if I can recheck >>> with a real scope too. >> >> We redid the tests with a real scope, and the signal looks nice >> and square, so it is not that. >> >> Speculating further on the cause of the long ACKs, I think that >> the i2c driver gets confused by an interrupt that marks the >> transfer complete, and thinks it's a NACK interrupt instead. Is that >> plausible? >> >> If the peripheral unit is such that it generates a stop automatically >> on NACKs, then this makes perfect sense. I.e. the TWI sees that the >> transfer is complete, generates an interrupt, and waits for further >> data or a stop command. Meanwhile the driver thinks it's a NACK and >> that a stop condition has already been sent to the bus, and just >> notifies the i2c consumer (the eeprom driver in this case) of the >> failure and frees up the bus for any future user. >> >> This also matches what I see when I turn on some more traffic on the >> bus, that is interleaved with the eeprom traffic. AFAICT, it can be >> any command that gets chewed up by the eeprom if it is sent to the >> i2c driver during the long ACK. >> >> Are you Atmel people making any progress on this data corrupting >> regression? Is there anything else I can do to help? >> >> Cheers, >> Peter >> > > Hi Peter, > > I have sent a patch to Ludovic for a first internal review before publishing > to > mainline. The patch should fix your issue since it fixes it on my sama5d36ek > board with an at24 eeprom. > > More details on the reason of this bug would be provided in both the commit > message and comments in the code provided by the reviewed patch but I you want > an early fix just read the Status Register (AT91_TWI_SR) at the beginning of > at91_do_twi_transfer(). This read clears the NACK bit in the Status Register. > Then the following source code can safely enable the NACK interrupt, otherwise > in some cases a pending NACK interrupt would rise immediately after the line: > at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK); > hence breaking the sequence of operations to be done because the interrupt > handler would call complete() too early so wait_for_completion_timeout() > also exits too early. > > So reading the Status Register at the beginning of at91_do_twi_transfer() > should be enough to fix the issue. Yes, I see no more long ACKs after that reading the Status Register there. Great! > Another mistake is in the interrupt handler itself, ie atmel_twi_interrupt(): > we should check the TWI_TXRDY status bit before calling > at91_twi_write_next_byte() only if both the TWI_TXCOMP and TWI_NACK status > bits > are clear. Otherwise, writing a new byte into the THR tells the I2C controller > to start a new transfer. Then the I2C slave, the at24 eeprom, is likely to > also reply by a second NACK. Hence the NACK bit is already set into the Status > Register on the next call of at91_do_twi_transfer(). > This is what I saw on my scope for PIO transfers. I interpret this as a proposed solution for the strange double NACKs? Anyway, I find it unnecessarily hard to grasp exactly what you mean (wasteful policy you are apparently suffering from where it is OK to publish a patch written in English, but apparently a big no-no to send a diff until it passes some internal review???). I interpreted your "patch" in English as: at91_twi_read_next_byte(dev); - else if (irqstatus & AT_TWI_TXRDY) + else if ((irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_TXRDY | AT91_TWI_NACK)) == AT91_TWI_TXRDY) at91_twi_write_next_byte(dev); But still see some double NACKs. Not always though, and it doesn't wreak any havoc. But it still looks strange and I can't explain them when looking at what the eeprom driver requests. Does this mean that there are more races present? Or, did I just parse your English "patch" badly? > By the way, in my case, the first NACK occurs because the at24 driver tries to > perform a second write transfer too quickly and the eeprom is not ready yet, > then replies with a NACK. Yes, I believe this is by design. Noone wants to encode the exact delays needed since they are
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-13 16:21, Ludovic Desroches wrote: > From: Cyrille Pitchen <cyrille.pitc...@atmel.com> > > In some cases a NACK interrupt may be pending in the Status Register (SR) > as a result of a previous transfer. However at91_do_twi_transfer() did not > read the SR to clear pending interruptions before starting a new transfer. > Hence a NACK interrupt rose as soon as it was enabled again at the I2C > controller level, resulting in a wrong sequence of operations and strange > patterns of behaviour on the I2C bus, such as a clock stretch followed by > a restart of the transfer. > > This first issue occurred with both DMA and PIO write transfers. > > Also when a NACK error was detected during a PIO write transfer, the > interrupt handler used to wrongly start a new transfer by writing into the > Transmit Holding Register (THR). Then the I2C slave was likely to reply > with a second NACK. > > This second issue is fixed in atmel_twi_interrupt() by handling the TXRDY > status bit only if both the TXCOMP and NACK status bits are cleared. > > Tested with a at24 eeprom on sama5d36ek board running a linux-4.1-at91 > kernel image. Adapted to linux-next. > > Signed-off-by: Cyrille Pitchen <cyrille.pitc...@atmel.com> > Fixes: 93563a6a71bb ("i2c: at91: fix a race condition when using the DMA > controller") > Reported-by: Peter Rosin <p...@lysator.liu.se> > Signed-off-by: Ludovic Desroches <ludovic.desroc...@atmel.com> > Cc: sta...@vger.kernel.org #4.1 The regression is gone with this patch (vanilla 4.2), thank you! However, looking at the bus, there are two NACKs after each successful chunk of memory written by the eeprom driver. Looking at the eeprom driver, I expect this on the bus: S 0x50 0x00 "hello there guys" P S 0x50 NACK P delay for at least 1 ms (since the eeprom driver has a msleep(1) call). S 0x50 NACK P delay for at least 1 ms ... ... S 0x50 NACK P delay for at least 1 ms S 0x50 0x10 "and girls\n" P This is not what I observe on the bus, most of the time there is a second NACK immediately following the first. I suspect that it is the i2c bus driver that somehow confuses itself and reissues the command for some reason? But this behavior has been there since the beginning, so it's probably orthogonal, and killing the data corrupting regression is much more important to me than fussing over a surplus failed transfer. Hence Tested-by: Peter Rosin <p...@lysator.liu.se> Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: Regression: at24 eeprom writing
On 2015-10-13 14:57, Nicolas Ferre wrote: > Le 13/10/2015 12:38, Peter Rosin a écrit : >> On 2015-10-12 18:13, Cyrille Pitchen wrote: >>> Le 12/10/2015 17:13, Peter Rosin a écrit : >>>> On 2015-10-05 17:09, Peter Rosin wrote: >>>>> But what trouble does the i2c bus driver see? Admittedly I only >>>>> have a simple logic level bus viewer, and not a full-blown >>>>> oscilloscope, so there might be something analogue going on? >>>>> I don't think so though, those signals looked fine last time we >>>>> looked (but we obviously didn't have these issues then, and >>>>> didn't really look that closely). I'll see if I can recheck >>>>> with a real scope too. >>>> >>>> We redid the tests with a real scope, and the signal looks nice >>>> and square, so it is not that. >>>> >>>> Speculating further on the cause of the long ACKs, I think that >>>> the i2c driver gets confused by an interrupt that marks the >>>> transfer complete, and thinks it's a NACK interrupt instead. Is that >>>> plausible? >>>> >>>> If the peripheral unit is such that it generates a stop automatically >>>> on NACKs, then this makes perfect sense. I.e. the TWI sees that the >>>> transfer is complete, generates an interrupt, and waits for further >>>> data or a stop command. Meanwhile the driver thinks it's a NACK and >>>> that a stop condition has already been sent to the bus, and just >>>> notifies the i2c consumer (the eeprom driver in this case) of the >>>> failure and frees up the bus for any future user. >>>> >>>> This also matches what I see when I turn on some more traffic on the >>>> bus, that is interleaved with the eeprom traffic. AFAICT, it can be >>>> any command that gets chewed up by the eeprom if it is sent to the >>>> i2c driver during the long ACK. >>>> >>>> Are you Atmel people making any progress on this data corrupting >>>> regression? Is there anything else I can do to help? >>>> >>>> Cheers, >>>> Peter >>>> >>> >>> Hi Peter, >>> >>> I have sent a patch to Ludovic for a first internal review before >>> publishing to >>> mainline. The patch should fix your issue since it fixes it on my sama5d36ek >>> board with an at24 eeprom. >>> >>> More details on the reason of this bug would be provided in both the commit >>> message and comments in the code provided by the reviewed patch but I you >>> want >>> an early fix just read the Status Register (AT91_TWI_SR) at the beginning of >>> at91_do_twi_transfer(). This read clears the NACK bit in the Status >>> Register. >>> Then the following source code can safely enable the NACK interrupt, >>> otherwise >>> in some cases a pending NACK interrupt would rise immediately after the >>> line: >>> at91_twi_write(dev, AT91_TWI_IER, AT91_TWI_NACK); >>> hence breaking the sequence of operations to be done because the interrupt >>> handler would call complete() too early so wait_for_completion_timeout() >>> also exits too early. >>> >>> So reading the Status Register at the beginning of at91_do_twi_transfer() >>> should be enough to fix the issue. >> >> Yes, I see no more long ACKs after that reading the Status Register there. >> Great! >> >>> Another mistake is in the interrupt handler itself, ie >>> atmel_twi_interrupt(): >>> we should check the TWI_TXRDY status bit before calling >>> at91_twi_write_next_byte() only if both the TWI_TXCOMP and TWI_NACK status >>> bits >>> are clear. Otherwise, writing a new byte into the THR tells the I2C >>> controller >>> to start a new transfer. Then the I2C slave, the at24 eeprom, is likely to >>> also reply by a second NACK. Hence the NACK bit is already set into the >>> Status >>> Register on the next call of at91_do_twi_transfer(). >>> This is what I saw on my scope for PIO transfers. >> >> I interpret this as a proposed solution for the strange double NACKs? >> >> Anyway, I find it unnecessarily hard to grasp exactly what you mean >> (wasteful policy you are apparently suffering from where it is OK to >> publish a patch written in English, but apparently a big no-no to >> send a diff until it passes some internal review???). I interpreted > > I find your remark pretty rude as I'm
Re: [PATCH] i2c: at91: fix write transfers by clearing pending interrupt first
On 2015-10-13 18:47, Cyrille Pitchen wrote: > Le 13/10/2015 17:19, Peter Rosin a écrit : >> On 2015-10-13 16:21, Ludovic Desroches wrote: >>> From: Cyrille Pitchen <cyrille.pitc...@atmel.com> >>> >>> In some cases a NACK interrupt may be pending in the Status Register (SR) >>> as a result of a previous transfer. However at91_do_twi_transfer() did not >>> read the SR to clear pending interruptions before starting a new transfer. >>> Hence a NACK interrupt rose as soon as it was enabled again at the I2C >>> controller level, resulting in a wrong sequence of operations and strange >>> patterns of behaviour on the I2C bus, such as a clock stretch followed by >>> a restart of the transfer. >>> >>> This first issue occurred with both DMA and PIO write transfers. >>> >>> Also when a NACK error was detected during a PIO write transfer, the >>> interrupt handler used to wrongly start a new transfer by writing into the >>> Transmit Holding Register (THR). Then the I2C slave was likely to reply >>> with a second NACK. >>> >>> This second issue is fixed in atmel_twi_interrupt() by handling the TXRDY >>> status bit only if both the TXCOMP and NACK status bits are cleared. >>> >>> Tested with a at24 eeprom on sama5d36ek board running a linux-4.1-at91 >>> kernel image. Adapted to linux-next. >>> >>> Signed-off-by: Cyrille Pitchen <cyrille.pitc...@atmel.com> >>> Fixes: 93563a6a71bb ("i2c: at91: fix a race condition when using the DMA >>> controller") >>> Reported-by: Peter Rosin <p...@lysator.liu.se> >>> Signed-off-by: Ludovic Desroches <ludovic.desroc...@atmel.com> >>> Cc: sta...@vger.kernel.org #4.1 >> >> The regression is gone with this patch (vanilla 4.2), thank you! >> >> However, looking at the bus, there are two NACKs after each >> successful chunk of memory written by the eeprom driver. >> >> Looking at the eeprom driver, I expect this on the bus: >> >> S 0x50 0x00 "hello there guys" P >> S 0x50 NACK P >> delay for at least 1 ms (since the eeprom driver has a msleep(1) call). >> S 0x50 NACK P >> delay for at least 1 ms >> ... >> ... >> S 0x50 NACK P >> delay for at least 1 ms >> S 0x50 0x10 "and girls\n" P >> >> This is not what I observe on the bus, most of the time there is a >> second NACK immediately following the first. I suspect that it is >> the i2c bus driver that somehow confuses itself and reissues the >> command for some reason? >> >> But this behavior has been there since the beginning, so it's probably >> orthogonal, and killing the data corrupting regression is much more >> important to me than fussing over a surplus failed transfer. Hence >> >> Tested-by: Peter Rosin <p...@lysator.liu.se> >> >> Cheers, >> Peter >> > > Hi Peter, > > sama5d3x and sama5d4x don't support the so called "Alternative Command mode" > whereas sama5d2x do. The Alternative Command mode comes with a new hardware > mechanism inside the I2C controller which locks the transmission of data on > the I2C bus when a NACK is detected. It means that even if a data is written > into the THR, the I2C controller doesn't push this data on the I2C bus but > retains the data in the THR (and its associated FIFO for sama5d2x and future > SoCs) until the driver unlocks the transmitter by writing the LOCKCLR (Lock > Clear) bit in the Control Register. Then and only then, the transmitter > outputs > pending data again. > This new mechanism was introduced to cope with an unwanted DMA write into the > THR after a NACK. Indeed, as I've tried to explain in my patch, when a first > NACK is detected, the I2C controller sets the TXCOMP, NACK and TXRDY bits > alltogether in the Status Register. However setting the TXRDY bit also > triggers > the DMA controller to write the next data into the THR. Pitifully, WITHOUT the > new lock mechanism, writing into the THR starts a new I2C frame. Since the > eeprom is likely not to be ready yet, it replies by a second NACK. So you > see on the scope two consecutive NACKs. > > On sama5d3x and sama5d4x, which do not support this lock mechanism, you are > likely to see a successful write transfer followed by two NACKs then a delay > and finally a new successful write transfer. This is the case 2b: > > 1 - A successfull write transfer is completed. > 2 - The at24 driver immediately tries to perform the next write transfer... > 3 - ... but the eeprom is not ready yet and replies with
Re: Regression: at24 eeprom writing
On 2015-10-03 01:05, Peter Rosin wrote: > I looked around and found that if I revert > a839ce663b3183209fdf7b1fc4796bfe2a4679c3 > "eeprom: at24: extend driver to allow writing via i2c_smbus_write_byte_data" > eeprom writing starts working again. > > AFAICT, the i2c-at91 bus driver makes the eeprom driver use the > i2c_transfer code path both with that patch and with it reverted, > so I sadly don't see why the patch makes a difference. And now when I retry the same thing, that patch is no longer affecting things. I must have confused myself over what kernel was actually running. Christian, please accept my deepest apologies for implicating you in this regression. But the regression is still there. In short, linux-3.18-at91 from the linux4sam tree works, linux-4.1-at91 from the same tree does not, and vanilla 4.2 also doesn't work. I have a hard time bisecting this thing though, since the last known good version has a long list of atmel patches that I refuse to even try to rebase... Ideas still welcome of course. Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: Regression: at24 eeprom writing
On 2015-10-03 01:05, Peter Rosin wrote: > Hi! > > I recently upgraded from the atmel linux-3.18-at91 kernel to vanilla 4.2 > and everything seemed fine. Until I tried to write to the little eeprom > chip. I then tried the linux-4.1-at91 kernel and that suffers too. > > The symptoms are that it seems like writes get interrupted, and restarted > again without properly initializing everything again. Inspecting the i2c > bus during these fails gets me something like this (int hex) when I > > echo abcdefghijklmnopqr > /sys/bus/i2c/devices/0-0050/eeprom > > S a0 00 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 P > S a0 10 (clk and data low for a "long" time) 10 71 72 0a P > > Notice how the address byte in the second chunk (10) is repeated after > the strange event on the i2c bus. > > I looked around and found that if I revert > a839ce663b3183209fdf7b1fc4796bfe2a4679c3 > "eeprom: at24: extend driver to allow writing via i2c_smbus_write_byte_data" > eeprom writing starts working again. > > AFAICT, the i2c-at91 bus driver makes the eeprom driver use the > i2c_transfer code path both with that patch and with it reverted, > so I sadly don't see why the patch makes a difference. > > I'm on a board that is based on the sama5d31 evaluation kit, with a > NXP SE97BTP,547 chip and this in the devicetree: > > i2c0: i2c@f0014000 { > status = "okay"; > > jc42@18 { > compatible = "jc42"; > reg = <0x18>; > }; > > eeprom@50 { > compatible = "24c02"; > reg = <0x50>; > pagesize = <16>; > }; > }; Ok, I found the culprit, and I double and triple checked it this time... If I move to the very latest on the linux-3.18-at91 branch, the bug is there too. Which made it vastly more palatable to bisect the bug. The offender (in the 4.2 kernel) is 93563a6a71bb69dd324fc7354c60fb05f84aae6b "i2c: at91: fix a race condition when using the DMA controller" which is far more understandable. Ao, adding Cyrille Pitchen to the Cc list. If I add that patch on top of my previously working tree, it behaves just as newer kernels, i.e. equally bad. The patch doesn't revert cleanly, but reverting the patch and quick-n-dirty-fixing the conflict on vanilla 4.2 makes the problem go away. I have attached what I actually reverted. Cheers, Peter >From d178e0636358e61503ac55d39c8612ef93c1d893 Mon Sep 17 00:00:00 2001 From: Peter Rosin <p...@axentia.se> Date: Mon, 5 Oct 2015 10:16:18 +0200 Subject: [PATCH] Revert "i2c: at91: fix a race condition when using the DMA controller" This reverts commit 93563a6a71bb69dd324fc7354c60fb05f84aae6b. Conflicts: drivers/i2c/busses/i2c-at91.c --- drivers/i2c/busses/i2c-at91.c | 97 + 1 file changed, 21 insertions(+), 76 deletions(-) diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 1c758cd1e1ba..b5a5ef26b142 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -74,9 +74,6 @@ #define AT91_TWI_NACK BIT(8) /* Not Acknowledged */ #define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */ -#define AT91_TWI_INT_MASK \ - (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK) - #define AT91_TWI_IER 0x0024 /* Interrupt Enable Register */ #define AT91_TWI_IDR 0x0028 /* Interrupt Disable Register */ #define AT91_TWI_IMR 0x002c /* Interrupt Mask Register */ @@ -155,12 +152,13 @@ static void at91_twi_write(struct at91_twi_dev *dev, unsigned reg, unsigned val) static void at91_disable_twi_interrupts(struct at91_twi_dev *dev) { - at91_twi_write(dev, AT91_TWI_IDR, AT91_TWI_INT_MASK); + at91_twi_write(dev, AT91_TWI_IDR, + AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY); } static void at91_twi_irq_save(struct at91_twi_dev *dev) { - dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & AT91_TWI_INT_MASK; + dev->imr = at91_twi_read(dev, AT91_TWI_IMR) & 0x7; at91_disable_twi_interrupts(dev); } @@ -255,16 +253,7 @@ static void at91_twi_write_data_dma_callback(void *data) dma_unmap_single(dev->dev, sg_dma_address(>dma.sg[0]), dev->buf_len, DMA_TO_DEVICE); - /* - * When this callback is called, THR/TX FIFO is likely not to be empty - * yet. So we have to wait for TXCOMP or NACK bits to be set into the - * Status Register to be sure that the STOP bit has been sent and the - * transfer is completed. The NACK interrupt has already been enable
Regression: at24 eeprom writing
Hi! I recently upgraded from the atmel linux-3.18-at91 kernel to vanilla 4.2 and everything seemed fine. Until I tried to write to the little eeprom chip. I then tried the linux-4.1-at91 kernel and that suffers too. The symptoms are that it seems like writes get interrupted, and restarted again without properly initializing everything again. Inspecting the i2c bus during these fails gets me something like this (int hex) when I echo abcdefghijklmnopqr > /sys/bus/i2c/devices/0-0050/eeprom S a0 00 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 P S a0 10 (clk and data low for a "long" time) 10 71 72 0a P Notice how the address byte in the second chunk (10) is repeated after the strange event on the i2c bus. I looked around and found that if I revert a839ce663b3183209fdf7b1fc4796bfe2a4679c3 "eeprom: at24: extend driver to allow writing via i2c_smbus_write_byte_data" eeprom writing starts working again. AFAICT, the i2c-at91 bus driver makes the eeprom driver use the i2c_transfer code path both with that patch and with it reverted, so I sadly don't see why the patch makes a difference. I'm on a board that is based on the sama5d31 evaluation kit, with a NXP SE97BTP,547 chip and this in the devicetree: i2c0: i2c@f0014000 { status = "okay"; jc42@18 { compatible = "jc42"; reg = <0x18>; }; eeprom@50 { compatible = "24c02"; reg = <0x50>; pagesize = <16>; }; }; Any ideas? Cheers, Peter -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v6 2/2] iio: mcp4531: Driver for Microchip digital potentiometers
On 2015-09-27 17:50, Jonathan Cameron wrote: > On 23/09/15 15:26, Peter Rosin wrote: >> From: Peter Rosin <p...@axentia.se> >> >> Add support for Microchip digital potentiometers and rheostats >> MCP4531, MCP4532, MCP4551, MCP4552 >> MCP4631, MCP4632, MCP4651, MCP4652 >> >> DEVICE Wipers Steps Resistor Opts (kOhm) i2c address >> MCP4531 1 1295, 10, 50, 100010111x >> MCP4532 1 1295, 10, 50, 10001011xx >> MCP4551 1 2575, 10, 50, 100010111x >> MCP4552 1 2575, 10, 50, 10001011xx >> MCP4631 2 1295, 10, 50, 1000101xxx >> MCP4632 2 1295, 10, 50, 10001011xx >> MCP4651 2 2575, 10, 50, 1000101xxx >> MCP4652 2 2575, 10, 50, 10001011xx >> >> Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf > Applied to the togreg branch of iio.git - initially pushed out as > testing for the autobuilders to play with it. > > If anyone wants to add reviewed-by / acked-by then as I'll be > rebasing sometime in next few days anyway there is still time! Great, thanks, but I don't see this where I expected it[1]. So, the question is if I'm too impatient, if am I looking in the wrong place or if you perhaps forgot to actually push it out? Or if something went totally wrong and the patches got lost... Cheers, Peter [1] http://git.kernel.org/cgit/linux/kernel/git/jic23/iio.git -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
[PATCH v5 2/2] iio: mcp4531: Driver for Microchip digital potentiometers
From: Peter Rosin <p...@axentia.se> Add support for Microchip digital potentiometers and rheostats MCP4531, MCP4532, MCP4551, MCP4552 MCP4631, MCP4632, MCP4651, MCP4652 DEVICE Wipers Steps Resistor Opts (kOhm) i2c address MCP4531 1 1295, 10, 50, 100010111x MCP4532 1 1295, 10, 50, 10001011xx MCP4551 1 2575, 10, 50, 100010111x MCP4552 1 2575, 10, 50, 10001011xx MCP4631 2 1295, 10, 50, 1000101xxx MCP4632 2 1295, 10, 50, 10001011xx MCP4651 2 2575, 10, 50, 1000101xxx MCP4652 2 2575, 10, 50, 10001011xx Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf Signed-off-by: Peter Rosin <p...@axentia.se> --- MAINTAINERS |6 + drivers/iio/Kconfig |1 + drivers/iio/Makefile|1 + drivers/iio/potentiometer/Kconfig | 20 +++ drivers/iio/potentiometer/Makefile |6 + drivers/iio/potentiometer/mcp4531.c | 228 +++ 6 files changed, 262 insertions(+) create mode 100644 drivers/iio/potentiometer/Kconfig create mode 100644 drivers/iio/potentiometer/Makefile create mode 100644 drivers/iio/potentiometer/mcp4531.c diff --git a/MAINTAINERS b/MAINTAINERS index b60e2b2369d2..27862156c7a7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6600,6 +6600,12 @@ W: http://linuxtv.org S: Maintained F: drivers/media/radio/radio-maxiradio* +MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVER +M: Peter Rosin <p...@axentia.se> +L: linux-...@vger.kernel.org +S: Maintained +F: drivers/iio/potentiometer/mcp4531.c + MEDIA DRIVERS FOR RENESAS - VSP1 M: Laurent Pinchart <laurent.pinch...@ideasonboard.com> L: linux-me...@vger.kernel.org diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 4011effe4c05..7cc87f322655 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -73,6 +73,7 @@ source "drivers/iio/orientation/Kconfig" if IIO_TRIGGER source "drivers/iio/trigger/Kconfig" endif #IIO_TRIGGER +source "drivers/iio/potentiometer/Kconfig" source "drivers/iio/pressure/Kconfig" source "drivers/iio/proximity/Kconfig" source "drivers/iio/temperature/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 698afc2d17ce..121c814e366b 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -23,6 +23,7 @@ obj-y += imu/ obj-y += light/ obj-y += magnetometer/ obj-y += orientation/ +obj-y += potentiometer/ obj-y += pressure/ obj-y += proximity/ obj-y += temperature/ diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig new file mode 100644 index ..fd75db73e582 --- /dev/null +++ b/drivers/iio/potentiometer/Kconfig @@ -0,0 +1,20 @@ +# +# Potentiometer drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Digital potentiometers" + +config MCP4531 + tristate "Microchip MCP45xx/MCP46xx Digital Potentiometer driver" + depends on I2C + help + Say yes here to build support for the Microchip + MCP4531, MCP4532, MCP4551, MCP4552, + MCP4631, MCP4632, MCP4651, MCP4652 + digital potentiomenter chips. + + To compile this driver as a module, choose M here: the + module will be called mcp4531. + +endmenu diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile new file mode 100644 index ..8afe49227012 --- /dev/null +++ b/drivers/iio/potentiometer/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for industrial I/O potentiometer drivers +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_MCP4531) += mcp4531.o diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c new file mode 100644 index ..82c24c6dcde0 --- /dev/null +++ b/drivers/iio/potentiometer/mcp4531.c @@ -0,0 +1,228 @@ +/* + * Industrial I/O driver for Microchip digital potentiometers + * Copyright (c) 2015 Axentia Technologies AB + * Author: Peter Rosin <p...@axentia.se> + * + * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf + * + * DEVID #Wipers #Positions Resistor Opts (kOhm)i2c address + * mcp4531 1 129 5, 10, 50, 100 010111x + * mcp4532 1 129 5, 10, 50, 100 01011xx + * mcp4551 1 257 5, 10, 50, 100 010111x + * mcp4552 1 257 5, 10, 50, 100 01011xx + * mcp4631 2 129 5, 10, 50, 100 0101xxx + * mcp4632 2 129 5, 10, 50, 100 01011xx + * mcp4651 2 257 5, 10, 50, 100 0101xxx + * mcp4652 2 257 5, 10, 50, 100
[PATCH v5 0/2] Driver for Microchip digital potentiometers
From: Peter Rosin <p...@axentia.se> This is the fifth attempt for a driver for these chips. Thanks for review comments from Greg Kroah-Hartman, Crt Mori, Daniel Baluta, Lars-Peter Clauson, Andreas Dannenberg, Peter Meerwald and Jonathan Cameron. I think and hope I got it all sorted. Changes since v4: - Less convoluted changelog (Jonathan) - Use an index into an array of configs as dev_id (Jonathan) - Drop .address and use .channel (Jonathan) - Remove one layer of wrappers (Jonathan) - Provide a scale (Jonathan, Lars-Peter) Changes since v3: - Use i2c_smbus_read_word_swapped (Peter) - Use devm_iio_device_register and drop the mcp4531_remove op (Peter) - Add defines for a few magic numbers (Peter) - Deduplicate channel params with a macro (Peter) - Mention the i2c client address options (Peter) - Whitespace and other trivial nits (Peter) Changes since v2: - Change naming from mcp4xxx_dpot to mcp4531 (Daniel) - Added links to datasheet in commit message and source (Daniel) - Rename from pot to potentiometer (Daniel, Crt) - Use IIO_RESISTANCE instead of IIO_STEPS (Crt, Lars-Peter) - Don't use wildcards in MAINTAINERS and point to the iio list (Crt) - Avoid races by not caching values (Crt) - Spell Microchip correctly (Andreas) Changes since v1: - Make it an IIO driver instead (Greg) - Don't convolute the code with big obscure macros (Greg) - Inline the bits from mcp4xxx_dpot.h that are actually used and drop that file (me) - Better Changelog (Greg) Cheers, Peter Peter Rosin (2): iio: resistance: Document that resistance can be output iio: mcp4531: Driver for Microchip digital potentiometers Documentation/ABI/testing/sysfs-bus-iio |2 + MAINTAINERS |6 + drivers/iio/Kconfig |1 + drivers/iio/Makefile|1 + drivers/iio/potentiometer/Kconfig | 20 +++ drivers/iio/potentiometer/Makefile |6 + drivers/iio/potentiometer/mcp4531.c | 228 +++ 7 files changed, 264 insertions(+) create mode 100644 drivers/iio/potentiometer/Kconfig create mode 100644 drivers/iio/potentiometer/Makefile create mode 100644 drivers/iio/potentiometer/mcp4531.c -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/