* [PATCH v2 4/4] ARM: dts: am4372: add DMA properties for tscadc
From: Mugunthan V N @ 2016-10-03 13:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003130318.12591-1-mugunthanvnm@ti.com>
Add DMA properties for tscadc
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
arch/arm/boot/dts/am4372.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 0fadae5..6094d17 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -867,6 +867,8 @@
clocks = <&adc_tsc_fck>;
clock-names = "fck";
status = "disabled";
+ dmas = <&edma 53 0>, <&edma 57 0>;
+ dma-names = "fifo0", "fifo1";
tsc {
compatible = "ti,am3359-tsc";
--
2.10.0.372.g6fe1b14
^ permalink raw reply related
* [PATCH v2 3/4] ARM: dts: am33xx: add DMA properties for tscadc
From: Mugunthan V N @ 2016-10-03 13:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003130318.12591-1-mugunthanvnm@ti.com>
Add DMA properties for tscadc
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
arch/arm/boot/dts/am33xx.dtsi | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi
index 98748c6..6d607b8 100644
--- a/arch/arm/boot/dts/am33xx.dtsi
+++ b/arch/arm/boot/dts/am33xx.dtsi
@@ -917,6 +917,8 @@
interrupts = <16>;
ti,hwmods = "adc_tsc";
status = "disabled";
+ dmas = <&edma 53 0>, <&edma 57 0>;
+ dma-names = "fifo0", "fifo1";
tsc {
compatible = "ti,am3359-tsc";
--
2.10.0.372.g6fe1b14
^ permalink raw reply related
* [PATCH v2 2/4] drivers: iio: ti_am335x_adc: add dma support
From: Mugunthan V N @ 2016-10-03 13:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003130318.12591-1-mugunthanvnm@ti.com>
This patch adds the required pieces to ti_am335x_adc driver for
DMA support
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/iio/adc/ti_am335x_adc.c | 145 ++++++++++++++++++++++++++++++++++-
include/linux/mfd/ti_am335x_tscadc.h | 7 ++
2 files changed, 149 insertions(+), 3 deletions(-)
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c3cfacca..7e250be 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -30,10 +30,28 @@
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE SZ_2K
+
+struct tiadc_dma {
+ struct dma_slave_config conf;
+ struct dma_chan *chan;
+ dma_addr_t addr;
+ dma_cookie_t cookie;
+ u8 *buf;
+ int current_period;
+ int period_size;
+ u8 fifo_thresh;
+};
+
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
+ struct tiadc_dma dma;
struct mutex fifo1_lock; /* to protect fifo access */
int channels;
+ int total_ch_enabled;
u8 channel_line[8];
u8 channel_step[8];
int buffer_en_ch_steps;
@@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
return IRQ_HANDLED;
}
+static void tiadc_dma_rx_complete(void *param)
+{
+ struct iio_dev *indio_dev = param;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ u8 *data;
+ int i;
+
+ data = dma->buf + dma->current_period * dma->period_size;
+ dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
+
+ for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
+ iio_push_to_buffers(indio_dev, data);
+ data += indio_dev->scan_bytes;
+ }
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ struct dma_async_tx_descriptor *desc;
+
+ dma->current_period = 0; /* We start to fill period 0 */
+ /*
+ * Make the fifo thresh as the multiple of total number of
+ * channels enabled, so make sure that cyclic DMA period
+ * length is also a multiple of total number of channels
+ * enabled. This ensures that no invalid data is reported
+ * to the stack via iio_push_to_buffers().
+ */
+ dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
+ adc_dev->total_ch_enabled) - 1;
+ /* Make sure that period length is multiple of fifo thresh level */
+ dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
+ (dma->fifo_thresh + 1) * sizeof(u16));
+
+ dma->conf.src_maxburst = dma->fifo_thresh + 1;
+ dmaengine_slave_config(dma->chan, &dma->conf);
+
+ desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+ dma->period_size * 2,
+ dma->period_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = tiadc_dma_rx_complete;
+ desc->callback_param = indio_dev;
+
+ dma->cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dma->chan);
+
+ tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+ return 0;
+}
+
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
@@ -218,20 +297,30 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ unsigned int irq_enable;
unsigned int enb = 0;
u8 bit;
tiadc_step_config(indio_dev);
- for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels) {
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
+ adc_dev->total_ch_enabled++;
+ }
adc_dev->buffer_en_ch_steps = enb;
+ if (dma->chan)
+ tiadc_start_dma(indio_dev);
+
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
- tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
- | IRQENB_FIFO1OVRRUN);
+
+ irq_enable = IRQENB_FIFO1OVRRUN;
+ if (!dma->chan)
+ irq_enable |= IRQENB_FIFO1THRES;
+ tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
return 0;
}
@@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
int fifo1count, i, read;
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
adc_dev->buffer_en_ch_steps = 0;
+ adc_dev->total_ch_enabled = 0;
+ if (dma->chan) {
+ tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+ dmaengine_terminate_async(dma->chan);
+ }
/* Flush FIFO of leftover data in the time it takes to disable adc */
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@@ -430,6 +525,38 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};
+static int tiadc_request_dma(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct tiadc_dma *dma = &adc_dev->dma;
+ dma_cap_mask_t mask;
+
+ /* Default slave configuration parameters */
+ dma->conf.direction = DMA_DEV_TO_MEM;
+ dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get a channel for RX */
+ dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
+ if (!dma->chan)
+ return -ENODEV;
+
+ /* RX buffer */
+ dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ &dma->addr, GFP_KERNEL);
+ if (!dma->buf)
+ goto err;
+
+ return 0;
+err:
+ dma_release_channel(dma->chan);
+
+ return -ENOMEM;
+}
+
static int tiadc_parse_dt(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
@@ -512,8 +639,14 @@ static int tiadc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
+ err = tiadc_request_dma(pdev, adc_dev);
+ if (err && err != -ENODEV)
+ goto err_dma;
+
return 0;
+err_dma:
+ iio_device_unregister(indio_dev);
err_buffer_unregister:
tiadc_iio_buffered_hardware_remove(indio_dev);
err_free_channels:
@@ -525,8 +658,14 @@ static int tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
u32 step_en;
+ if (dma->chan) {
+ dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ dma->buf, dma->addr);
+ dma_release_channel(dma->chan);
+ }
iio_device_unregister(indio_dev);
tiadc_iio_buffered_hardware_remove(indio_dev);
tiadc_channels_remove(indio_dev);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index e45a208..b9a53e0 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -23,6 +23,8 @@
#define REG_IRQENABLE 0x02C
#define REG_IRQCLR 0x030
#define REG_IRQWAKEUP 0x034
+#define REG_DMAENABLE_SET 0x038
+#define REG_DMAENABLE_CLEAR 0x03c
#define REG_CTRL 0x040
#define REG_ADCFSM 0x044
#define REG_CLKDIV 0x04C
@@ -36,6 +38,7 @@
#define REG_FIFO0THR 0xE8
#define REG_FIFO1CNT 0xF0
#define REG_FIFO1THR 0xF4
+#define REG_DMA1REQ 0xF8
#define REG_FIFO0 0x100
#define REG_FIFO1 0x200
@@ -126,6 +129,10 @@
#define FIFOREAD_DATA_MASK (0xfff << 0)
#define FIFOREAD_CHNLID_MASK (0xf << 16)
+/* DMA ENABLE/CLEAR Register */
+#define DMA_FIFO0 BIT(0)
+#define DMA_FIFO1 BIT(1)
+
/* Sequencer Status */
#define SEQ_STATUS BIT(5)
#define CHARGE_STEP 0x11
--
2.10.0.372.g6fe1b14
^ permalink raw reply related
* [PATCH v2 1/4] mfd: ti_am335x_tscadc: store physical address
From: Mugunthan V N @ 2016-10-03 13:03 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003130318.12591-1-mugunthanvnm@ti.com>
store the physical address of the device in its priv to use it
for DMA addressing in the client drivers.
Signed-off-by: Mugunthan V N <mugunthanvnm@ti.com>
---
drivers/mfd/ti_am335x_tscadc.c | 1 +
include/linux/mfd/ti_am335x_tscadc.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index c8f027b..0f3fab4 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
tscadc->irq = err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_phys_base = res->start;
tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tscadc->tscadc_base))
return PTR_ERR(tscadc->tscadc_base);
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index 7f55b8b..e45a208 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -155,6 +155,7 @@ struct ti_tscadc_dev {
struct device *dev;
struct regmap *regmap;
void __iomem *tscadc_base;
+ phys_addr_t tscadc_phys_base;
int irq;
int used_cells; /* 1-2 */
int tsc_wires;
--
2.10.0.372.g6fe1b14
^ permalink raw reply related
* [PATCH v2 0/4] Add DMA support for ti_am335x_adc driver
From: Mugunthan V N @ 2016-10-03 13:03 UTC (permalink / raw)
To: linux-arm-kernel
The ADC has a 64 work depth fifo length which holds the ADC data
till the CPU reads. So when a user program needs a large ADC data
to operate on, then it has to do multiple reads to get its
buffer. Currently if the application asks for 4 samples per
channel with all 8 channels are enabled, kernel can provide only
3 samples per channel when all 8 channels are enabled (logs at
[1]). So with DMA support user can request for large number of
samples at a time (logs at [2]).
Tested the patch on AM437x-gp-evm and AM335x Boneblack with the
patch [3] to enable ADC and pushed a branch for testing [4]
Changes from Initial version:
* Changed DMA api from dma_request_slave_channel_compat() to
dma_request_chan()
* Changed variable names to have more clear information as per
comments from Peter.
[1] - http://pastebin.ubuntu.com/23211490/
[2] - http://pastebin.ubuntu.com/23269792/
[3] - http://pastebin.ubuntu.com/23211494/
[4] - git://git.ti.com/~mugunthanvnm/ti-linux-kernel/linux.git iio-dma-v2
Mugunthan V N (4):
mfd: ti_am335x_tscadc: store physical address
drivers: iio: ti_am335x_adc: add dma support
ARM: dts: am33xx: add DMA properties for tscadc
ARM: dts: am4372: add DMA properties for tscadc
arch/arm/boot/dts/am33xx.dtsi | 2 +
arch/arm/boot/dts/am4372.dtsi | 2 +
drivers/iio/adc/ti_am335x_adc.c | 145 ++++++++++++++++++++++++++++++++++-
drivers/mfd/ti_am335x_tscadc.c | 1 +
include/linux/mfd/ti_am335x_tscadc.h | 8 ++
5 files changed, 155 insertions(+), 3 deletions(-)
--
2.10.0.372.g6fe1b14
^ permalink raw reply
* [PATCH] drm/sun4i: Check that the plane coordinates are not negative
From: Maxime Ripard @ 2016-10-03 12:58 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160930180826.169e3daf@bbrezillon>
Hi Boris,
On Fri, Sep 30, 2016 at 06:08:26PM +0200, Boris Brezillon wrote:
> On Fri, 30 Sep 2016 16:33:20 +0200
> Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
>
> > Our planes cannot be set at negative coordinates. Make sure we reject such
> > configuration.
> >
> > Reported-by: Boris Brezillon <boris.brezillon@free-electrons.com>
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> > drivers/gpu/drm/sun4i/sun4i_layer.c | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/sun4i/sun4i_layer.c b/drivers/gpu/drm/sun4i/sun4i_layer.c
> > index f0035bf5efea..f5463c4c2cde 100644
> > --- a/drivers/gpu/drm/sun4i/sun4i_layer.c
> > +++ b/drivers/gpu/drm/sun4i/sun4i_layer.c
> > @@ -29,6 +29,9 @@ struct sun4i_plane_desc {
> > static int sun4i_backend_layer_atomic_check(struct drm_plane *plane,
> > struct drm_plane_state *state)
> > {
> > + if ((state->crtc_x < 0) || (state->crtc_y < 0))
> > + return -EINVAL;
> > +
>
> Hm, I think it's a perfectly valid use case from the DRM framework and
> DRM user PoV: you may want to place your plane at a negative CRTC
> offset (which means part of the plane will be hidden).
>
> Maybe I'm wrong, but it seems you can support that by adapting the
> start address of your framebuffer pointer and the layer size.
Indeed, that would probably work. This is even somewhat what we've
been using to implement the VGA hack we use on the CHIP.
Can you send that patch?
Thanks,
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161003/32b3fe92/attachment.sig>
^ permalink raw reply
* [PATCH] Adding Support for Coresight Components on Zynq 7000.
From: Muhammad Abdul WAHAB @ 2016-10-03 12:56 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160930165325.GA7630@xsjsorenbubuntu>
Hi S?ren,
> I tried to refresh my Zynq knowledge a bit. The clkc provides the
> dbg_trc clock, and that is the clock you need (not fclk). I couldn't
> find it in the binding (I guess I messed that up), but apparently,
> you can provide a 'trace_emio_clk' as input to the clkc node in the
> Zynq DT. Then, with the muxes correctly configured (FSBL should do
> that if you select the EMIO trace clock in Vivado), the dbg_trc
> output of the clkc should be that EMIO clock. And the dbg_trc output
> of the clkc is what should be consumed by the tpiu node. Though, as
> I see it the binding/driver for the TPIU do not support that.
>
> I.e.
> In the clkc description you'd have to add 'trace_emio_clk' to the
> clock-names property together with a matching reference in the 'clocks'
> property. As this change would be specific to local setups, this is not
> really appropriate for upstream.
>
> Then, for the trace clock, ideally the TPIU would consume and enable it
> as needed.
Thank you very much for this. I will have a look into it.
Below is the patch without TPIU, is it possible to submit it ? I will
submit the TPIU part very soon once I manage to get it working.
> Unfortunately, this is not how it works. The DT bindings are not a
> recommendation. The DT description must follow the binding, otherwise
> drivers will not work correctly, or best case, just ignore what you put
> there.
Thanks. The idea of including TPIU part was to get feedback as I am far
from being an expert on DT.
> As I don't see this in the coresight binding, I doubt that it has any
> effect or should be here.
That's what I was thinking also. I will re-look into TPIU part and send
it soon. Besides, if I want to ask you a question regarding TPIU or DT,
can I contact you alone or should I keep sending it to all the CS/DT
maintainers ?
Thanks,
M.Abdul WAHAB
---
--- linux-4.7/arch/arm/boot/dts/zynq-7000.dtsi.orig 2016-07-24
21:23:50.000000000 +0200
+++ linux-4.7/arch/arm/boot/dts/zynq-7000.dtsi 2016-10-03
14:38:00.164515838 +0200
@@ -96,6 +96,57 @@
rx-fifo-depth = <0x40>;
};
+ etb at F8801000 {
+ compatible = "arm,coresight-etb10", "arm,primecell";
+ reg = <0xf8801000 0x1000>;
+
+ coresight-default-sink;
+ clocks = <&clkc 47>;
+ clock-names = "apb_pclk";
+
+ port {
+ etb_in_port: endpoint at 0 {
+ slave-mode;
+ remote-endpoint = <&replicator_out_port0>;
+ };
+ };
+ };
+
+ funnel at F8804000 {
+ compatible = "arm,coresight-funnel", "arm,primecell";
+ reg = <0xf8804000 0x1000>;
+
+ clocks = <&clkc 47>;
+ clock-names = "apb_pclk";
+ ports {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ port at 0 {
+ reg = <0x0>;
+ funnel_out_port0: endpoint {
+ remote-endpoint = <&replicator_in_port0>;
+ };
+ };
+
+ port at 1 {
+ reg = <0x0>;
+ funnel_in_port0: endpoint {
+ slave-mode;
+ remote-endpoint = <&ptm0_out_port>;
+ };
+ };
+
+ port at 2 {
+ reg = <0x1>;
+ funnel_in_port1: endpoint {
+ slave-mode;
+ remote-endpoint = <&ptm1_out_port>;
+ };
+ };
+ };
+ };
+
gpio0: gpio at e000a000 {
compatible = "xlnx,zynq-gpio-1.0";
#gpio-cells = <2>;
@@ -311,6 +362,67 @@
clocks = <&clkc 4>;
};
+ ptm0 at F889C000 {
+ compatible = "arm,coresight-etm3x", "arm,primecell";
+ reg = <0xf889c000 0x1000>;
+
+ cpu = <&cpu0>;
+ clocks = <&clkc 47>;
+ clock-names = "apb_pclk";
+
+ port {
+ ptm0_out_port: endpoint {
+ remote-endpoint = <&funnel_in_port0>;
+ };
+ };
+ };
+
+ ptm1 at F889D000 {
+ compatible = "arm,coresight-etm3x", "arm,primecell";
+ reg = <0xf889d000 0x1000>;
+
+ cpu = <&cpu1>;
+ clocks = <&clkc 47>;
+ clock-names = "apb_pclk";
+
+ port {
+ ptm1_out_port: endpoint {
+ remote-endpoint = <&funnel_in_port1>;
+ };
+ };
+ };
+
+ replicator {
+ compatible = "arm,coresight-replicator";
+
+ ports {
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+
+ port at 0 {
+ reg = <0x0>;
+ replicator_out_port0: endpoint {
+ remote-endpoint = <&etb_in_port>;
+ };
+ };
+
+ port at 1 {
+ reg = <0x1>;
+ replicator_out_port1: endpoint {
+ remote-endpoint = <&tpiu_in_port>;
+ };
+ };
+
+ port at 2 {
+ reg = <0x0>;
+ replicator_in_port0: endpoint {
+ slave-mode;
+ remote-endpoint = <&funnel_out_port0>;
+ };
+ };
+ };
+ };
+
ttc0: timer at f8001000 {
interrupt-parent = <&intc>;
interrupts = <0 10 4>,
^ permalink raw reply
* [PATCH v1 2/2] spi: spi-fsl-dspi: Add DMA support for Vybrid
From: Sanchayan Maity @ 2016-10-03 12:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <63fc108e2df00d2297a9b7014955f203bc802f34.1475498805.git.maitysanchayan@gmail.com>
Add DMA support for Vybrid.
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
---
drivers/spi/spi-fsl-dspi.c | 293 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 293 insertions(+)
diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c
index 9e9dadb..ada50ee 100644
--- a/drivers/spi/spi-fsl-dspi.c
+++ b/drivers/spi/spi-fsl-dspi.c
@@ -15,6 +15,8 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
@@ -40,6 +42,7 @@
#define TRAN_STATE_WORD_ODD_NUM 0x04
#define DSPI_FIFO_SIZE 4
+#define DSPI_DMA_BUFSIZE (DSPI_FIFO_SIZE * 1024)
#define SPI_MCR 0x00
#define SPI_MCR_MASTER (1 << 31)
@@ -71,6 +74,11 @@
#define SPI_SR_EOQF 0x10000000
#define SPI_SR_TCFQF 0x80000000
+#define SPI_RSER_TFFFE BIT(25)
+#define SPI_RSER_TFFFD BIT(24)
+#define SPI_RSER_RFDFE BIT(17)
+#define SPI_RSER_RFDFD BIT(16)
+
#define SPI_RSER 0x30
#define SPI_RSER_EOQFE 0x10000000
#define SPI_RSER_TCFQE 0x80000000
@@ -108,6 +116,8 @@
#define SPI_TCR_TCNT_MAX 0x10000
+#define DMA_COMPLETION_TIMEOUT msecs_to_jiffies(3000)
+
struct chip_data {
u32 mcr_val;
u32 ctar_val;
@@ -117,6 +127,7 @@ struct chip_data {
enum dspi_trans_mode {
DSPI_EOQ_MODE = 0,
DSPI_TCFQ_MODE,
+ DSPI_DMA_MODE,
};
struct fsl_dspi_devtype_data {
@@ -139,6 +150,22 @@ static const struct fsl_dspi_devtype_data ls2085a_data = {
.max_clock_factor = 8,
};
+struct fsl_dspi_dma {
+ u32 curr_xfer_len;
+
+ u32 *tx_dma_buf;
+ struct dma_chan *chan_tx;
+ dma_addr_t tx_dma_phys;
+ struct completion cmd_tx_complete;
+ struct dma_async_tx_descriptor *tx_desc;
+
+ u32 *rx_dma_buf;
+ struct dma_chan *chan_rx;
+ dma_addr_t rx_dma_phys;
+ struct completion cmd_rx_complete;
+ struct dma_async_tx_descriptor *rx_desc;
+};
+
struct fsl_dspi {
struct spi_master *master;
struct platform_device *pdev;
@@ -165,6 +192,7 @@ struct fsl_dspi {
u32 waitflags;
u32 spi_tcnt;
+ struct fsl_dspi_dma *dma;
};
static inline int is_double_byte_mode(struct fsl_dspi *dspi)
@@ -368,6 +396,264 @@ static void dspi_tcfq_read(struct fsl_dspi *dspi)
dspi_data_from_popr(dspi, rx_word);
}
+static void dspi_tx_dma_callback(void *arg)
+{
+ struct fsl_dspi *dspi = arg;
+ struct fsl_dspi_dma *dma = dspi->dma;
+
+ complete(&dma->cmd_tx_complete);
+}
+
+static void dspi_rx_dma_callback(void *arg)
+{
+ struct fsl_dspi *dspi = arg;
+ struct fsl_dspi_dma *dma = dspi->dma;
+ int rx_word;
+ int i, len;
+ u16 d;
+
+ rx_word = is_double_byte_mode(dspi);
+
+ len = rx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len;
+
+ if (!(dspi->dataflags & TRAN_STATE_RX_VOID)) {
+ for (i = 0; i < len; i++) {
+ d = dspi->dma->rx_dma_buf[i];
+ rx_word ? (*(u16 *)dspi->rx = d) :
+ (*(u8 *)dspi->rx = d);
+ dspi->rx += rx_word + 1;
+ }
+ }
+
+ complete(&dma->cmd_rx_complete);
+}
+
+static int dspi_next_xfer_dma_submit(struct fsl_dspi *dspi)
+{
+ struct fsl_dspi_dma *dma = dspi->dma;
+ struct device *dev = &dspi->pdev->dev;
+ int time_left;
+ int tx_word;
+ int i, len;
+ u16 val;
+
+ tx_word = is_double_byte_mode(dspi);
+
+ len = tx_word ? (dma->curr_xfer_len / 2) : dma->curr_xfer_len;
+
+ for (i = 0; i < len - 1; i++) {
+ val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx;
+ dspi->dma->tx_dma_buf[i] =
+ SPI_PUSHR_TXDATA(val) | SPI_PUSHR_PCS(dspi->cs) |
+ SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
+ dspi->tx += tx_word + 1;
+ }
+
+ val = tx_word ? *(u16 *) dspi->tx : *(u8 *) dspi->tx;
+ dspi->dma->tx_dma_buf[i] = SPI_PUSHR_TXDATA(val) |
+ SPI_PUSHR_PCS(dspi->cs) |
+ SPI_PUSHR_CTAS(0);
+ dspi->tx += tx_word + 1;
+
+ dma->tx_desc = dmaengine_prep_slave_single(dma->chan_tx,
+ dma->tx_dma_phys,
+ DSPI_DMA_BUFSIZE, DMA_MEM_TO_DEV,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!dma->tx_desc) {
+ dev_err(dev, "Not able to get desc for DMA xfer\n");
+ return -EIO;
+ }
+
+ dma->tx_desc->callback = dspi_tx_dma_callback;
+ dma->tx_desc->callback_param = dspi;
+ if (dma_submit_error(dmaengine_submit(dma->tx_desc))) {
+ dev_err(dev, "DMA submit failed\n");
+ return -EINVAL;
+ }
+
+ dma->rx_desc = dmaengine_prep_slave_single(dma->chan_rx,
+ dma->rx_dma_phys,
+ DSPI_DMA_BUFSIZE, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!dma->rx_desc) {
+ dev_err(dev, "Not able to get desc for DMA xfer\n");
+ return -EIO;
+ }
+
+ dma->rx_desc->callback = dspi_rx_dma_callback;
+ dma->rx_desc->callback_param = dspi;
+ if (dma_submit_error(dmaengine_submit(dma->rx_desc))) {
+ dev_err(dev, "DMA submit failed\n");
+ return -EINVAL;
+ }
+
+ reinit_completion(&dspi->dma->cmd_rx_complete);
+ reinit_completion(&dspi->dma->cmd_tx_complete);
+
+ dma_async_issue_pending(dma->chan_rx);
+ dma_async_issue_pending(dma->chan_tx);
+
+ time_left = wait_for_completion_timeout(&dspi->dma->cmd_tx_complete,
+ DMA_COMPLETION_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(dev, "DMA tx timeout\n");
+ dmaengine_terminate_all(dma->chan_tx);
+ dmaengine_terminate_all(dma->chan_rx);
+ return -ETIMEDOUT;
+ }
+
+ time_left = wait_for_completion_timeout(&dspi->dma->cmd_rx_complete,
+ DMA_COMPLETION_TIMEOUT);
+ if (time_left == 0) {
+ dev_err(dev, "DMA rx timeout\n");
+ dmaengine_terminate_all(dma->chan_tx);
+ dmaengine_terminate_all(dma->chan_rx);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int dspi_dma_xfer(struct fsl_dspi *dspi)
+{
+ struct fsl_dspi_dma *dma = dspi->dma;
+ struct device *dev = &dspi->pdev->dev;
+ int curr_remaining_bytes;
+ int ret = 0;
+
+ curr_remaining_bytes = dspi->len;
+ while (curr_remaining_bytes) {
+ regmap_write(dspi->regmap, SPI_RSER, 0);
+ regmap_write(dspi->regmap, SPI_RSER,
+ SPI_RSER_TFFFE | SPI_RSER_TFFFD |
+ SPI_RSER_RFDFE | SPI_RSER_RFDFD);
+
+ /* Check if current transfer fits the DMA buffer */
+ dma->curr_xfer_len = curr_remaining_bytes;
+ if (curr_remaining_bytes > DSPI_DMA_BUFSIZE / sizeof(u32))
+ dma->curr_xfer_len = DSPI_DMA_BUFSIZE / sizeof(u32);
+
+ ret = dspi_next_xfer_dma_submit(dspi);
+ if (ret) {
+ dev_err(dev, "DMA transfer failed\n");
+ goto exit;
+
+ } else {
+ curr_remaining_bytes -= dma->curr_xfer_len;
+ if (curr_remaining_bytes < 0)
+ curr_remaining_bytes = 0;
+ dspi->len = curr_remaining_bytes;
+ }
+ }
+
+exit:
+ return ret;
+}
+
+static int dspi_request_dma(struct fsl_dspi *dspi, phys_addr_t phy_addr)
+{
+ struct fsl_dspi_dma *dma;
+ struct dma_slave_config cfg;
+ struct device *dev = &dspi->pdev->dev;
+ int ret;
+
+ dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ dma->chan_rx = dma_request_slave_channel(dev, "rx");
+ if (!dma->chan_rx) {
+ dev_err(dev, "rx dma channel not available\n");
+ ret = -ENODEV;
+ return ret;
+ }
+
+ dma->chan_tx = dma_request_slave_channel(dev, "tx");
+ if (!dma->chan_tx) {
+ dev_err(dev, "tx dma channel not available\n");
+ ret = -ENODEV;
+ goto err_tx_channel;
+ }
+
+ dma->tx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE,
+ &dma->tx_dma_phys, GFP_KERNEL);
+ if (!dma->tx_dma_buf) {
+ ret = -ENOMEM;
+ goto err_tx_dma_buf;
+ }
+
+ dma->rx_dma_buf = dma_alloc_coherent(dev, DSPI_DMA_BUFSIZE,
+ &dma->rx_dma_phys, GFP_KERNEL);
+ if (!dma->rx_dma_buf) {
+ ret = -ENOMEM;
+ goto err_rx_dma_buf;
+ }
+
+ cfg.src_addr = phy_addr + SPI_POPR;
+ cfg.dst_addr = phy_addr + SPI_PUSHR;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = 1;
+ cfg.dst_maxburst = 1;
+
+ cfg.direction = DMA_DEV_TO_MEM;
+ ret = dmaengine_slave_config(dma->chan_rx, &cfg);
+ if (ret) {
+ dev_err(dev, "can't configure rx dma channel\n");
+ ret = -EINVAL;
+ goto err_slave_config;
+ }
+
+ cfg.direction = DMA_MEM_TO_DEV;
+ ret = dmaengine_slave_config(dma->chan_tx, &cfg);
+ if (ret) {
+ dev_err(dev, "can't configure tx dma channel\n");
+ ret = -EINVAL;
+ goto err_slave_config;
+ }
+
+ dspi->dma = dma;
+ dspi->devtype_data->trans_mode = DSPI_DMA_MODE;
+ init_completion(&dma->cmd_tx_complete);
+ init_completion(&dma->cmd_rx_complete);
+
+ return 0;
+
+err_slave_config:
+ devm_kfree(dev, dma->rx_dma_buf);
+err_rx_dma_buf:
+ devm_kfree(dev, dma->tx_dma_buf);
+err_tx_dma_buf:
+ dma_release_channel(dma->chan_tx);
+err_tx_channel:
+ dma_release_channel(dma->chan_rx);
+
+ devm_kfree(dev, dma);
+ dspi->dma = NULL;
+
+ return ret;
+}
+
+static void dspi_release_dma(struct fsl_dspi *dspi)
+{
+ struct fsl_dspi_dma *dma = dspi->dma;
+ struct device *dev = &dspi->pdev->dev;
+
+ if (dma) {
+ if (dma->chan_tx) {
+ dma_unmap_single(dev, dma->tx_dma_phys,
+ DSPI_DMA_BUFSIZE, DMA_TO_DEVICE);
+ dma_release_channel(dma->chan_tx);
+ }
+
+ if (dma->chan_rx) {
+ dma_unmap_single(dev, dma->rx_dma_phys,
+ DSPI_DMA_BUFSIZE, DMA_FROM_DEVICE);
+ dma_release_channel(dma->chan_rx);
+ }
+ }
+}
+
static int dspi_transfer_one_message(struct spi_master *master,
struct spi_message *message)
{
@@ -424,6 +710,9 @@ static int dspi_transfer_one_message(struct spi_master *master,
regmap_write(dspi->regmap, SPI_RSER, SPI_RSER_TCFQE);
dspi_tcfq_write(dspi);
break;
+ case DSPI_DMA_MODE:
+ status = dspi_dma_xfer(dspi);
+ goto out;
default:
dev_err(&dspi->pdev->dev, "unsupported trans_mode %u\n",
trans_mode);
@@ -730,6 +1019,9 @@ static int dspi_probe(struct platform_device *pdev)
}
clk_prepare_enable(dspi->clk);
+ if (dspi_request_dma(dspi, res->start))
+ dev_warn(&pdev->dev, "can't get dma channels\n");
+
master->max_speed_hz =
clk_get_rate(dspi->clk) / dspi->devtype_data->max_clock_factor;
@@ -758,6 +1050,7 @@ static int dspi_remove(struct platform_device *pdev)
struct fsl_dspi *dspi = spi_master_get_devdata(master);
/* Disconnect from the SPI framework */
+ dspi_release_dma(dspi);
clk_disable_unprepare(dspi->clk);
spi_unregister_master(dspi->master);
spi_master_put(dspi->master);
--
2.10.0
^ permalink raw reply related
* [PATCH v1 1/2] ARM: dts: vfxxx: Enable DMA for DSPI on Vybrid
From: Sanchayan Maity @ 2016-10-03 12:50 UTC (permalink / raw)
To: linux-arm-kernel
Enable DMA for DSPI on Vybrid.
Signed-off-by: Sanchayan Maity <maitysanchayan@gmail.com>
---
arch/arm/boot/dts/vf-colibri.dtsi | 4 ++++
arch/arm/boot/dts/vfxxx.dtsi | 6 ++++++
2 files changed, 10 insertions(+)
diff --git a/arch/arm/boot/dts/vf-colibri.dtsi b/arch/arm/boot/dts/vf-colibri.dtsi
index b741709..21bfef9 100644
--- a/arch/arm/boot/dts/vf-colibri.dtsi
+++ b/arch/arm/boot/dts/vf-colibri.dtsi
@@ -108,6 +108,10 @@
status = "okay";
};
+&edma1 {
+ status = "okay";
+};
+
&esdhc1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_esdhc1>;
diff --git a/arch/arm/boot/dts/vfxxx.dtsi b/arch/arm/boot/dts/vfxxx.dtsi
index 2c13ec6..eac4213 100644
--- a/arch/arm/boot/dts/vfxxx.dtsi
+++ b/arch/arm/boot/dts/vfxxx.dtsi
@@ -194,6 +194,9 @@
clocks = <&clks VF610_CLK_DSPI0>;
clock-names = "dspi";
spi-num-chipselects = <6>;
+ dmas = <&edma1 1 12>,
+ <&edma1 1 13>;
+ dma-names = "rx", "tx";
status = "disabled";
};
@@ -206,6 +209,9 @@
clocks = <&clks VF610_CLK_DSPI1>;
clock-names = "dspi";
spi-num-chipselects = <4>;
+ dmas = <&edma1 1 14>,
+ <&edma1 1 15>;
+ dma-names = "rx", "tx";
status = "disabled";
};
--
2.10.0
^ permalink raw reply related
* [PATCH v26 0/7] arm64: add kdump support
From: Manish Jaggi @ 2016-10-03 12:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110424.GD14025@linaro.org>
On 10/03/2016 04:34 PM, AKASHI Takahiro wrote:
> Manish,
>
> On Mon, Oct 03, 2016 at 01:24:34PM +0530, Manish Jaggi wrote:
>> Hi Akashi,
>>
>> On 09/07/2016 09:59 AM, AKASHI Takahiro wrote:
>>> v26-specific note: After a comment from Rob[0], an idea of adding
>>> "linux,usable-memory-range" was dropped. Instead, an existing
>>> "reserved-memory" node will be used to limit usable memory ranges
>>> on crash dump kernel.
>>> This works not only on UEFI/ACPI systems but also on DT-only systems,
>>> but if he really insists on using DT-specific "usable-memory" property,
>>> I will post additional patches for kexec-tools. Those would be
>>> redundant, though.
>>> Even in that case, the kernel will not have to be changed.
>>>
>>> This patch series adds kdump support on arm64.
>>> There are some prerequisite patches [1],[2].
>>>
>>> To load a crash-dump kernel to the systems, a series of patches to
>>> kexec-tools, which have not yet been merged upstream, are needed.
>>> Please always use my latest kdump patches, v3 [3].
>>>
>>> To examine vmcore (/proc/vmcore) on a crash-dump kernel, you can use
>>> - crash utility (coming v7.1.6 or later) [4]
>>> (Necessary patches have already been queued in the master.)
>>>
>>>
>>> [0] http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/452582.html
>>> [1] "arm64: mark reserved memblock regions explicitly in iomem"
>>> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/450433.html
>>> [2] "efi: arm64: treat regions with WT/WC set but WB cleared as memory"
>>> http://lists.infradead.org/pipermail/linux-arm-kernel/2016-August/451491.html
>>> [3] T.B.D.
>>> [4] https://github.com/crash-utility/crash.git
>>>
>>
>> With the v26 kdump and v3 kexec-tools and top of tree crash.git, below are the tests done
>> Attached is a patch in crash.git (symbols.c) to make crash utility work on my setup.
>> Can you please have a look and provide your comments.
>>
>> To generate a panic, i have a kernel module which on init calls panic.
>>
>> Observations:
>> 1.1. Dump capture kernel shows different memory map.
>> ---------------------------------------------------
>> In dump capture kernel /proc/meminfo and /proc/iomem differ
>>
>> root at arm64:/home/ubuntu/CODE/crash#
>> MemTotal: 65882432 kB
>> MemFree: 65507136 kB
>> MemAvailable: 60373632 kB
>> Buffers: 29248 kB
>> Cached: 46720 kB
>> SwapCached: 0 kB
>> Active: 63872 kB
>> Inactive: 19776 kB
>> Active(anon): 8256 kB
>> Inactive(anon): 7616 kB
>>
>> First kernel is booted with mem=2G crashkernel=1G command line option.
>> While the system has 64G memory.
>>
>> root at arm64:/home/ubuntu/CODE/crash# cat /proc/iomem
>> 41400000-fffeffff : System RAM
>> 41480000-420cffff : Kernel code
>> 42490000-4278ffff : Kernel data
>> ffff0000-ffffffff : reserved
>> 100000000-ffaa7ffff : System RAM
>> ffaa80000-ffaabffff : reserved
>> ffaac0000-fffa6ffff : System RAM
>> fffa70000-fffacffff : reserved
>> fffad0000-fffffffff : System RAM
>
> Are you saying that "mem=..." doesn't have any effect?
What I am saying it that If the first kernel is booted using mem= option and crashkernel= option
the memory for second kernel has to be withing the crashkernel size.
As per /proc/iomem System RAM the information is correct, but the /proc/meminfo is showing total memory
much more than the first kernel had in first place.
> What about if you don't specify "crashkernel=...?"
>
In that case the second kernel will not boot as kexec tools will complain that memory not reserved.
>> 1.2 Live crash dump fails with error
>> --------------------------------------
>> $crash vmlinux
>>
>> crash 7.1.5++
>> Copyright (C) 2002-2016 Red Hat, Inc.
>> Copyright (C) 2004, 2005, 2006, 2010 IBM Corporation
>> Copyright (C) 1999-2006 Hewlett-Packard Co
>> Copyright (C) 2005, 2006, 2011, 2012 Fujitsu Limited
>> Copyright (C) 2006, 2007 VA Linux Systems Japan K.K.
>> Copyright (C) 2005, 2011 NEC Corporation
>> Copyright (C) 1999, 2002, 2007 Silicon Graphics, Inc.
>> Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
>> This program is free software, covered by the GNU General Public License,
>> and you are welcome to change it and/or distribute copies of it under
>> certain conditions. Enter "help copying" to see the conditions.
>> This program has absolutely no warranty. Enter "help warranty" for details.
>>
>> GNU gdb (GDB) 7.6
>> Copyright (C) 2013 Free Software Foundation, Inc.
>> License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
>> This is free software: you are free to change and redistribute it.
>> There is NO WARRANTY, to the extent permitted by law. Type "show copying"
>> and "show warranty" for details.
>> This GDB was configured as "aarch64-unknown-linux-gnu"...
>>
>> crash: read error: kernel virtual address: ffff800ffffffcc0 type: "pglist node_id"
>
> I have no ideas here.
If I run with debug logs phys address accessed is > 64G. (10413ffcc0)
Could be that somehow 64 + 1G + (addr) = 10413ffcc0 and actually addr was required.
addr = 413ffcc0 which seems in line with 424b0c50
Logs:
<read_dev_mem: addr: ffff0000090b3008 paddr: 424b3008 cnt: 8>
node_online_map: [1] -> nodes online: 1
<readmem: ffff0000090b0c50, KVADDR, ""node_data"", 8, (ROE), ffffc330eb00>
<read_dev_mem: addr: ffff0000090b0c50 paddr: 424b0c50 cnt: 8>
<readmem: ffff800ffffffcc0, KVADDR, ""pglist node_id"", 4, (FOE), ffffc330f1e4>
<read_dev_mem: addr: ffff800ffffffcc0 paddr: 10413ffcc0 cnt: 4>
/dev/mem: Bad address
crash: read(/dev/mem, 10413ffcc0, 4): 4294967295 (ffffffff)
crash: read error: kernel virtual address: ffff800ffffffcc0 type: ""pglist node_id""
"
>
>> Observation 2
>> ------------
>> If saved vmcore file is used
>>
>> $crash vmlinux vmcore_saved
>> Got the below error.
>>
>> please wait... (gathering module symbol data)crash: malloc.c:2846: mremap_chunk: Assertion `((size + offset) & (_rtld_global_ro._dl_pagesize - 1)) == 0' failed.
>> Aborted
>
> I have no ideas here.
>
>> Experiment 3
>> ------------
>> If crash.git is modified with a hack patch in symbols.c. Crash utility works fine log, bt commands work.
>
> In which case, "crash vmlinux" or "crash vmlinux vmcore_saved?"
>
vmcore_saved
> I was able to reproduce this issue in the latter case
> (but with a different error message).
> It seems to be a crash util's bug.
> Please report it to crash-util mailing list.
> I will post a patch.
The same patch as below ?
Can you please share your patch
>
> Thanks,
> -Takahiro AKASHI
>
>> -------------------
>> Patch: symbols.c
>> git diff symbols.c
>> diff --git a/symbols.c b/symbols.c
>> index 13282f4..f7c6cac 100644
>> --- a/symbols.c
>> +++ b/symbols.c
>> @@ -2160,6 +2160,7 @@ store_module_kallsyms_v2(struct load_module *lm, int start
>> FREEBUF(module_buf);
>> return 0;
>> }
>> + lm->mod_init_size = 0;
>>
>> if (lm->mod_init_size > 0) {
>> module_buf_init = GETBUF(lm->mod_init_size);
>> ------------------
>>
>> $ crash vmlinux vmcore_saved
>> KERNEL: /home/ubuntu/CODE/linux/vmlinux
>> DUMPFILE: vm
>> CPUS: 48 [OFFLINE: 46]
>> DATE: Mon Oct 3 00:11:47 2016
>> UPTIME: 00:02:41
>> LOAD AVERAGE: 0.36, 0.14, 0.05
>> TASKS: 171
>> NODENAME: arm64
>> RELEASE: 4.8.0-rc3-00044-g070a615-dirty
>> VERSION: #63 SMP Sat Oct 1 01:39:45 PDT 2016
>> MACHINE: aarch64 (unknown Mhz)
>> MEMORY: 2 GB
>> PANIC: "Kernel panic - not syncing: crash module starting"
>> PID: 958
>> COMMAND: "insmod"
>> TASK: ffff800007859300 [THREAD_INFO: ffff80000c940000]
>> CPU: 0
>> STATE: TASK_RUNNING (PANIC)
>>
>> crash> bt
>> PID: 958 TASK: ffff800007859300 CPU: 0 COMMAND: "insmod"
>> #0 [ffff80000c943980] __crash_kexec at ffff000008144fe8
>> #1 [ffff80000c943ae0] panic at ffff0000081ae704
>> #2 [ffff80000c943ba0] init_module at ffff000000900014 [crash]
>> #3 [ffff80000c943bb0] do_one_initcall at ffff000008083bb4
>> #4 [ffff80000c943c40] do_init_module at ffff0000081af6f0
>> #5 [ffff80000c943c70] load_module at ffff000008140b7c
>> #6 [ffff80000c943e10] sys_finit_module at ffff000008141634
>> #7 [ffff80000c943ed0] el0_svc_naked at ffff0000080833ec
>> PC: 00000003 LR: ffffaca050a0 SP: ffffaca865a0 PSTATE: 00000111
>> X12: ffffac941a5c X11: 00000080 X10: 00000004 X9: 00000030
>> X8: ffffffff X7: fefefefefefeff40 X6: 00000111 X5: 00000001
>> X4: 00000001 X3: 0002ed61 X2: 00000000 X1: 00000003
>> X0: 00000000
>> crash>
>>
>>
>> ---
>> Thanks,
>> manish
>>
^ permalink raw reply
* [PATCH v2 1/1] crypto: atmel-aes: add support to the XTS mode
From: Cyrille Pitchen @ 2016-10-03 12:33 UTC (permalink / raw)
To: linux-arm-kernel
This patch adds the xts(aes) algorithm, which is supported from
hardware version 0x500 and above (sama5d2x).
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
---
ChangeLog:
v1 -> v2:
- fix typo in comment inside atmel_aes_xts_process_data():
s/reverted/reversed/
- use xts_check_key() from atmel_aes_xts_setkey() as suggested by
Stephan Mueller.
drivers/crypto/atmel-aes-regs.h | 4 +
drivers/crypto/atmel-aes.c | 185 ++++++++++++++++++++++++++++++++++++++--
2 files changed, 183 insertions(+), 6 deletions(-)
diff --git a/drivers/crypto/atmel-aes-regs.h b/drivers/crypto/atmel-aes-regs.h
index 6c2951bb70b1..0ec04407b533 100644
--- a/drivers/crypto/atmel-aes-regs.h
+++ b/drivers/crypto/atmel-aes-regs.h
@@ -28,6 +28,7 @@
#define AES_MR_OPMOD_CFB (0x3 << 12)
#define AES_MR_OPMOD_CTR (0x4 << 12)
#define AES_MR_OPMOD_GCM (0x5 << 12)
+#define AES_MR_OPMOD_XTS (0x6 << 12)
#define AES_MR_LOD (0x1 << 15)
#define AES_MR_CFBS_MASK (0x7 << 16)
#define AES_MR_CFBS_128b (0x0 << 16)
@@ -67,6 +68,9 @@
#define AES_CTRR 0x98
#define AES_GCMHR(x) (0x9c + ((x) * 0x04))
+#define AES_TWR(x) (0xc0 + ((x) * 0x04))
+#define AES_ALPHAR(x) (0xd0 + ((x) * 0x04))
+
#define AES_HW_VERSION 0xFC
#endif /* __ATMEL_AES_REGS_H__ */
diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index 1d9e7bd3f377..6b656f4a9378 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -36,6 +36,7 @@
#include <crypto/scatterwalk.h>
#include <crypto/algapi.h>
#include <crypto/aes.h>
+#include <crypto/xts.h>
#include <crypto/internal/aead.h>
#include <linux/platform_data/crypto-atmel.h>
#include <dt-bindings/dma/at91.h>
@@ -68,6 +69,7 @@
#define AES_FLAGS_CFB8 (AES_MR_OPMOD_CFB | AES_MR_CFBS_8b)
#define AES_FLAGS_CTR AES_MR_OPMOD_CTR
#define AES_FLAGS_GCM AES_MR_OPMOD_GCM
+#define AES_FLAGS_XTS AES_MR_OPMOD_XTS
#define AES_FLAGS_MODE_MASK (AES_FLAGS_OPMODE_MASK | \
AES_FLAGS_ENCRYPT | \
@@ -89,6 +91,7 @@ struct atmel_aes_caps {
bool has_cfb64;
bool has_ctr32;
bool has_gcm;
+ bool has_xts;
u32 max_burst_size;
};
@@ -135,6 +138,12 @@ struct atmel_aes_gcm_ctx {
atmel_aes_fn_t ghash_resume;
};
+struct atmel_aes_xts_ctx {
+ struct atmel_aes_base_ctx base;
+
+ u32 key2[AES_KEYSIZE_256 / sizeof(u32)];
+};
+
struct atmel_aes_reqctx {
unsigned long mode;
};
@@ -282,6 +291,20 @@ static const char *atmel_aes_reg_name(u32 offset, char *tmp, size_t sz)
snprintf(tmp, sz, "GCMHR[%u]", (offset - AES_GCMHR(0)) >> 2);
break;
+ case AES_TWR(0):
+ case AES_TWR(1):
+ case AES_TWR(2):
+ case AES_TWR(3):
+ snprintf(tmp, sz, "TWR[%u]", (offset - AES_TWR(0)) >> 2);
+ break;
+
+ case AES_ALPHAR(0):
+ case AES_ALPHAR(1):
+ case AES_ALPHAR(2):
+ case AES_ALPHAR(3):
+ snprintf(tmp, sz, "ALPHAR[%u]", (offset - AES_ALPHAR(0)) >> 2);
+ break;
+
default:
snprintf(tmp, sz, "0x%02x", offset);
break;
@@ -453,15 +476,15 @@ static inline int atmel_aes_complete(struct atmel_aes_dev *dd, int err)
return err;
}
-static void atmel_aes_write_ctrl(struct atmel_aes_dev *dd, bool use_dma,
- const u32 *iv)
+static void atmel_aes_write_ctrl_key(struct atmel_aes_dev *dd, bool use_dma,
+ const u32 *iv, const u32 *key, int keylen)
{
u32 valmr = 0;
/* MR register must be set before IV registers */
- if (dd->ctx->keylen == AES_KEYSIZE_128)
+ if (keylen == AES_KEYSIZE_128)
valmr |= AES_MR_KEYSIZE_128;
- else if (dd->ctx->keylen == AES_KEYSIZE_192)
+ else if (keylen == AES_KEYSIZE_192)
valmr |= AES_MR_KEYSIZE_192;
else
valmr |= AES_MR_KEYSIZE_256;
@@ -478,13 +501,19 @@ static void atmel_aes_write_ctrl(struct atmel_aes_dev *dd, bool use_dma,
atmel_aes_write(dd, AES_MR, valmr);
- atmel_aes_write_n(dd, AES_KEYWR(0), dd->ctx->key,
- SIZE_IN_WORDS(dd->ctx->keylen));
+ atmel_aes_write_n(dd, AES_KEYWR(0), key, SIZE_IN_WORDS(keylen));
if (iv && (valmr & AES_MR_OPMOD_MASK) != AES_MR_OPMOD_ECB)
atmel_aes_write_block(dd, AES_IVR(0), iv);
}
+static inline void atmel_aes_write_ctrl(struct atmel_aes_dev *dd, bool use_dma,
+ const u32 *iv)
+
+{
+ atmel_aes_write_ctrl_key(dd, use_dma, iv,
+ dd->ctx->key, dd->ctx->keylen);
+}
/* CPU transfer */
@@ -1769,6 +1798,137 @@ static struct aead_alg aes_gcm_alg = {
};
+/* xts functions */
+
+static inline struct atmel_aes_xts_ctx *
+atmel_aes_xts_ctx_cast(struct atmel_aes_base_ctx *ctx)
+{
+ return container_of(ctx, struct atmel_aes_xts_ctx, base);
+}
+
+static int atmel_aes_xts_process_data(struct atmel_aes_dev *dd);
+
+static int atmel_aes_xts_start(struct atmel_aes_dev *dd)
+{
+ struct atmel_aes_xts_ctx *ctx = atmel_aes_xts_ctx_cast(dd->ctx);
+ struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+ struct atmel_aes_reqctx *rctx = ablkcipher_request_ctx(req);
+ unsigned long flags;
+ int err;
+
+ atmel_aes_set_mode(dd, rctx);
+
+ err = atmel_aes_hw_init(dd);
+ if (err)
+ return atmel_aes_complete(dd, err);
+
+ /* Compute the tweak value from req->info with ecb(aes). */
+ flags = dd->flags;
+ dd->flags &= ~AES_FLAGS_MODE_MASK;
+ dd->flags |= (AES_FLAGS_ECB | AES_FLAGS_ENCRYPT);
+ atmel_aes_write_ctrl_key(dd, false, NULL,
+ ctx->key2, ctx->base.keylen);
+ dd->flags = flags;
+
+ atmel_aes_write_block(dd, AES_IDATAR(0), req->info);
+ return atmel_aes_wait_for_data_ready(dd, atmel_aes_xts_process_data);
+}
+
+static int atmel_aes_xts_process_data(struct atmel_aes_dev *dd)
+{
+ struct ablkcipher_request *req = ablkcipher_request_cast(dd->areq);
+ bool use_dma = (req->nbytes >= ATMEL_AES_DMA_THRESHOLD);
+ u32 tweak[AES_BLOCK_SIZE / sizeof(u32)];
+ static const u32 one[AES_BLOCK_SIZE / sizeof(u32)] = {cpu_to_le32(1), };
+ u8 *tweak_bytes = (u8 *)tweak;
+ int i;
+
+ /* Read the computed ciphered tweak value. */
+ atmel_aes_read_block(dd, AES_ODATAR(0), tweak);
+ /*
+ * Hardware quirk:
+ * the order of the ciphered tweak bytes need to be reversed before
+ * writing them into the ODATARx registers.
+ */
+ for (i = 0; i < AES_BLOCK_SIZE/2; ++i) {
+ u8 tmp = tweak_bytes[AES_BLOCK_SIZE - 1 - i];
+
+ tweak_bytes[AES_BLOCK_SIZE - 1 - i] = tweak_bytes[i];
+ tweak_bytes[i] = tmp;
+ }
+
+ /* Process the data. */
+ atmel_aes_write_ctrl(dd, use_dma, NULL);
+ atmel_aes_write_block(dd, AES_TWR(0), tweak);
+ atmel_aes_write_block(dd, AES_ALPHAR(0), one);
+ if (use_dma)
+ return atmel_aes_dma_start(dd, req->src, req->dst, req->nbytes,
+ atmel_aes_transfer_complete);
+
+ return atmel_aes_cpu_start(dd, req->src, req->dst, req->nbytes,
+ atmel_aes_transfer_complete);
+}
+
+static int atmel_aes_xts_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct atmel_aes_xts_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+ int err;
+
+ err = xts_check_key(crypto_ablkcipher_tfm(tfm), key, keylen);
+ if (err)
+ return err;
+
+ memcpy(ctx->base.key, key, keylen/2);
+ memcpy(ctx->key2, key + keylen/2, keylen/2);
+ ctx->base.keylen = keylen/2;
+
+ return 0;
+}
+
+static int atmel_aes_xts_encrypt(struct ablkcipher_request *req)
+{
+ return atmel_aes_crypt(req, AES_FLAGS_XTS | AES_FLAGS_ENCRYPT);
+}
+
+static int atmel_aes_xts_decrypt(struct ablkcipher_request *req)
+{
+ return atmel_aes_crypt(req, AES_FLAGS_XTS);
+}
+
+static int atmel_aes_xts_cra_init(struct crypto_tfm *tfm)
+{
+ struct atmel_aes_xts_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ tfm->crt_ablkcipher.reqsize = sizeof(struct atmel_aes_reqctx);
+ ctx->base.start = atmel_aes_xts_start;
+
+ return 0;
+}
+
+static struct crypto_alg aes_xts_alg = {
+ .cra_name = "xts(aes)",
+ .cra_driver_name = "atmel-xts-aes",
+ .cra_priority = ATMEL_AES_PRIORITY,
+ .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize = sizeof(struct atmel_aes_xts_ctx),
+ .cra_alignmask = 0xf,
+ .cra_type = &crypto_ablkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_init = atmel_aes_xts_cra_init,
+ .cra_exit = atmel_aes_cra_exit,
+ .cra_u.ablkcipher = {
+ .min_keysize = 2 * AES_MIN_KEY_SIZE,
+ .max_keysize = 2 * AES_MAX_KEY_SIZE,
+ .ivsize = AES_BLOCK_SIZE,
+ .setkey = atmel_aes_xts_setkey,
+ .encrypt = atmel_aes_xts_encrypt,
+ .decrypt = atmel_aes_xts_decrypt,
+ }
+};
+
+
/* Probe functions */
static int atmel_aes_buff_init(struct atmel_aes_dev *dd)
@@ -1877,6 +2037,9 @@ static void atmel_aes_unregister_algs(struct atmel_aes_dev *dd)
{
int i;
+ if (dd->caps.has_xts)
+ crypto_unregister_alg(&aes_xts_alg);
+
if (dd->caps.has_gcm)
crypto_unregister_aead(&aes_gcm_alg);
@@ -1909,8 +2072,16 @@ static int atmel_aes_register_algs(struct atmel_aes_dev *dd)
goto err_aes_gcm_alg;
}
+ if (dd->caps.has_xts) {
+ err = crypto_register_alg(&aes_xts_alg);
+ if (err)
+ goto err_aes_xts_alg;
+ }
+
return 0;
+err_aes_xts_alg:
+ crypto_unregister_aead(&aes_gcm_alg);
err_aes_gcm_alg:
crypto_unregister_alg(&aes_cfb64_alg);
err_aes_cfb64_alg:
@@ -1928,6 +2099,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd)
dd->caps.has_cfb64 = 0;
dd->caps.has_ctr32 = 0;
dd->caps.has_gcm = 0;
+ dd->caps.has_xts = 0;
dd->caps.max_burst_size = 1;
/* keep only major version number */
@@ -1937,6 +2109,7 @@ static void atmel_aes_get_cap(struct atmel_aes_dev *dd)
dd->caps.has_cfb64 = 1;
dd->caps.has_ctr32 = 1;
dd->caps.has_gcm = 1;
+ dd->caps.has_xts = 1;
dd->caps.max_burst_size = 4;
break;
case 0x200:
--
2.7.4
^ permalink raw reply related
* [GIT PULL 1/3] ARM: soc: exynos: Drivers for v4.9
From: Kukjin Kim @ 2016-10-03 12:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003074825.GA2724@kozik-lap>
2016. 10. 3. 15:48 Krzysztof Kozlowski <krzk@kernel.org> wrote:
>> On Sun, Oct 02, 2016 at 05:25:07PM -0700, Olof Johansson wrote:
>>> On Mon, Sep 19, 2016 at 8:53 AM, Krzysztof Kozlowski <krzk@kernel.org> wrote:
>>>> On Mon, Sep 19, 2016 at 05:02:40PM +0200, Arnd Bergmann wrote:
>>>>> On Sunday, September 18, 2016 6:39:46 PM CEST Krzysztof Kozlowski wrote:
>>>>> Samsung drivers/soc update for v4.9:
>>>>> 1. Allow compile testing of exynos-mct clocksource driver on ARM64.
>>>>> 2. Document Exynos5433 PMU compatible (already used by clkout driver and more
>>>>> will be coming soon).
>>>>
>>>> Pulled into next/drivers, thanks
>>>>
>>>> Just for my understanding: why do we need the exynos-mct driver on ARM64
>>>> but not the delay-timer portion of it?
>>>
>>> I think we want all of it but Doug's optimization 3252a646aa2c
>>> ("clocksource: exynos_mct: Only use 32-bits where possible") is not
>>> ARM64 friendly. One way of dealing with it would be to prepare two
>>> versions of exynos4_read_current_timer(). One reading only lower 32-bit
>>> value for ARMv7 and second (slow) reading lower and upper for ARMv8.
>>>
>>>>
>>>> Is there an advantage in using MCT over the architected timer on these
>>>> chips? If so, should we also have a way to use it as the delay timer?
>>>
>>> No, there is no real advantage... except that the SoC has some
>>> interesting "characteristics"... The timers are tightly coupled. Very
>>> tightly. I spent a lot of time and failed to boot my ARMv8 board without
>>> some MCT magic.
>>
>> What kind of magic is that?
>
> Most notably: the arch timer starts when MCT forward running counter
> starts. Without kicking MCT, the arch timer seems to be frozen.
>
>> I can understand that needing the MCT for
>> some system-level timer functionality might be true (wakeups, etc),
>> but for system timesource avoiding the MMIO timer and using the arch
>> ones is a substantial performance improvement for gettimeofday() and
>> friends.
>>
>> There was extensive discussion last year over using arch timers on
>> 5420/5422, and it fizzled out with vague comments about something not
>> working right between A15/A7 on b.L. hardware. I'm presuming whatever
>> implementation details of that SoC has since been fixed on later chips
>> (including v8). Any chance you can confirm? It'd be very nice to leave
>> MCT behind on v8 as a system time source.
>
> Unfortunately, I cannot confirm this, at least on Exynos5433 (ARMv8). I
> played with arch and MCT timers on it and failed to get the
> arch-timer-only setup working. I did not have access to newer Exynos
> designs (Exynos 7) so I do not know how it works there.
Hi guys,
I know what Olof want to know and actually several days ago someone asked me about that. As you guys talked, a couple of years ago there were some discussions...BTW I need to contact to hardware designer before let you guys know because something needs to be confirmed by them even I know roughly.
Note I'm in vacation with my family. Will be back on this in several days with exact information.
BRs,
Kukjin
^ permalink raw reply
* [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
From: Maxime Ripard @ 2016-10-03 11:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-6-wens@csie.org>
Hi,
On Mon, Oct 03, 2016 at 07:07:57PM +0800, Chen-Yu Tsai wrote:
> The A31 has a similar codec to the A10/A20. The PCM parts are very
> similar, with just different register offsets. The analog paths are
> very different. There are more inputs and outputs.
>
> The quirks structure is expanded to include different register offsets
> and separate callbacks for creating the ASoC card. Also the DMA burst
> length is increased to 8. While the A10 DMA engine supports bursts of
> 1, 4 and 8, the A31 engine only supports 1 and 8.
>
> This patch adds support for the basic playback path of the A31 codec,
> from the DAC to the headphones. Headphone detection, microphone,
> signaling, other inputs/outputs and capture will be added later.
>
> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
> ---
> .../devicetree/bindings/sound/sun4i-codec.txt | 6 +-
> sound/soc/sunxi/sun4i-codec.c | 396 +++++++++++++++++----
> 2 files changed, 334 insertions(+), 68 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> index 0dce690f78f5..1d2411cea98d 100644
> --- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> +++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
> @@ -1,8 +1,10 @@
> * Allwinner A10 Codec
>
> Required properties:
> -- compatible: must be either "allwinner,sun4i-a10-codec" or
> - "allwinner,sun7i-a20-codec"
> +- compatible: must be one of the following compatibles:
> + - "allwinner,sun4i-a10-codec"
> + - "allwinner,sun6i-a31-codec"
> + - "allwinner,sun7i-a20-codec"
I'm guessing it needs a reset line?
> - reg: must contain the registers location and length
> - interrupts: must contain the codec interrupt
> - dmas: DMA channels for tx and rx dma. See the DMA client binding,
> diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
> index e047ec06d538..9916714ecb71 100644
> --- a/sound/soc/sunxi/sun4i-codec.c
> +++ b/sound/soc/sunxi/sun4i-codec.c
> @@ -3,6 +3,7 @@
> * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
> * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
> * Copyright 2015 Adam Sampson <ats@offog.org>
> + * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
> *
> * Based on the Allwinner SDK driver, released under the GPL.
> *
> @@ -24,8 +25,9 @@
> #include <linux/delay.h>
> #include <linux/slab.h>
> #include <linux/of.h>
> -#include <linux/of_platform.h>
> #include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> #include <linux/clk.h>
> #include <linux/regmap.h>
> #include <linux/gpio/consumer.h>
> @@ -55,6 +57,8 @@
> #define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
> #define SUN4I_CODEC_DAC_FIFOS (0x08)
> #define SUN4I_CODEC_DAC_TXDATA (0x0c)
> +
> +/* Codec DAC side analog signal controls */
> #define SUN4I_CODEC_DAC_ACTL (0x10)
> #define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
> #define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
> @@ -81,6 +85,8 @@
> #define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
> #define SUN4I_CODEC_ADC_FIFOS (0x20)
> #define SUN4I_CODEC_ADC_RXDATA (0x24)
> +
> +/* Codec ADC side analog signal controls */
> #define SUN4I_CODEC_ADC_ACTL (0x28)
> #define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
> #define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
> @@ -93,18 +99,106 @@
> #define SUN4I_CODEC_ADC_ACTL_DDE (3)
> #define SUN4I_CODEC_ADC_DEBUG (0x2c)
>
> -/* Other various ADC registers */
> +/* FIFO counters */
> #define SUN4I_CODEC_DAC_TXCNT (0x30)
> #define SUN4I_CODEC_ADC_RXCNT (0x34)
> +
> +/* Other various ADC registers */
> #define SUN7I_CODEC_AC_DAC_CAL (0x38)
> #define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)
>
> +/*** sun6i specific register offsets ***/
> +#define SUN6I_CODEC_ADC_FIFOC (0x10)
> +#define SUN6I_CODEC_ADC_FIFOC_EN_AD (28)
> +#define SUN6I_CODEC_ADC_FIFOS (0x14)
> +#define SUN6I_CODEC_ADC_RXDATA (0x18)
> +#define SUN6I_CODEC_OM_DACA_CTRL (0x20)
> +#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31)
> +#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8)
> +#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7)
> +#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6)
> +#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0)
> +#define SUN6I_CODEC_OM_PA_CTRL (0x24)
> +#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31)
> +#define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15)
> +#define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12)
> +#define SUN6I_CODEC_OM_PA_CTRL_LINEING (9)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3)
> +#define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0)
> +#define SUN6I_CODEC_MIC_CTRL (0x28)
> +#define SUN6I_CODEC_MIC_CTRL_HBIASEN (31)
> +#define SUN6I_CODEC_MIC_CTRL_MBIASEN (30)
> +#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28)
> +#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21)
> +#define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16)
> +#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11)
> +#define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8)
> +#define SUN6I_CODEC_ADC_ACTL (0x2c)
> +#define SUN6I_CODEC_ADC_ACTL_ADCREN (31)
> +#define SUN6I_CODEC_ADC_ACTL_ADCLEN (30)
> +#define SUN6I_CODEC_ADC_ACTL_ADCRG (27)
> +#define SUN6I_CODEC_ADC_ACTL_ADCLG (24)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8)
> +#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1)
> +#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0)
> +#define SUN6I_CODEC_ADDA_TUNE (0x30)
> +#define SUN6I_CODEC_CALIBRATION (0x34)
> +#define SUN6I_CODEC_DAC_TXCNT (0x40)
> +#define SUN6I_CODEC_ADC_RXCNT (0x44)
> +#define SUN6I_CODEC_HMIC_CTL (0x50)
> +#define SUN6I_CODEC_HMIC_DATA (0x54)
> +
> +/* TODO sun6i DAP (Digital Audio Processing) bits */
> +
> +struct sun4i_codec_regs {
> + u32 adc_fifoc;
> + u32 adc_fifos;
> + u32 adc_rxdata;
> +};
> +
> struct sun4i_codec {
> struct device *dev;
> struct regmap *regmap;
> struct clk *clk_apb;
> struct clk *clk_module;
> struct gpio_desc *gpio_pa;
> + const struct sun4i_codec_regs *regs;
You're reimplementing reg_field here.
>
> struct snd_dmaengine_dai_dma_data capture_dma_data;
> struct snd_dmaengine_dai_dma_data playback_dma_data;
> @@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
> static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
> {
> /* Enable ADC DRQ */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
> BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
> }
> @@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
> static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
> {
> /* Disable ADC DRQ */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
> }
>
> @@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
>
>
> /* Flush RX FIFO */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
> BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
>
>
> /* Set RX FIFO trigger level */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> 0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
> 0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
>
> @@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
> * Allwinner's code mentions that it is related
> * related to microphone gain
> */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
> - 0x3 << 25,
> - 0x1 << 25);
> + if (!of_device_is_compatible(scodec->dev->of_node,
> + "allwinner,sun6i-a31-codec")) {
> + regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
> + 0x3 << 25,
> + 0x1 << 25);
> + }
>
> if (of_device_is_compatible(scodec->dev->of_node,
> "allwinner,sun7i-a20-codec"))
> @@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
> 0x1 << 8);
>
> /* Fill most significant bits with valid data MSB */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
> BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
>
> @@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
> unsigned int hwrate)
> {
> /* Set ADC sample rate */
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> 7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
> hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
>
> /* Set the number of channels we want to use */
> if (params_channels(params) == 1)
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
> BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
> else
> - regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
> + regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
> BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
>
> return 0;
> @@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
> BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
> BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
>
> - /* Set TX FIFO mode to padding the LSBs with 0 */
> + /* Use higher 24 bits of TX FIFO */
> regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
> BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
> 0);
> @@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
> BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
> 0);
>
> - /* Set TX FIFO mode to repeat the MSB */
> + /* Use lower 16 bits of TX FIFO */
> regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
> BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
> BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
> @@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
> },
> };
>
> -/*** Codec ***/
> +/*** sun4i Codec ***/
> static const struct snd_kcontrol_new sun4i_codec_pa_mute =
> SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
> SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
> @@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = {
> },
> };
>
> +/*** sun6i Codec ***/
> +
> +/* mixer controls */
> +static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
> + SOC_DAPM_DOUBLE("DAC Playback Switch",
> + SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
> + SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
> + SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
> + SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
> + SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
> +};
> +
> +/* headphone controls */
> +static const char * const sun6i_codec_hp_src_enum_text[] = {
> + "DAC", "Mixer",
> +};
> +
> +static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
> + SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
> + SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
> + sun6i_codec_hp_src_enum_text);
> +
> +static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
> + SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
> +};
> +
> +/* volume / mute controls */
> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
> +static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
> +
> +static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
> + SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
> + SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
> + sun6i_codec_dvol_scale),
> + SOC_SINGLE_TLV("Headphone Playback Volume",
> + SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
> + sun6i_codec_hp_vol_scale),
> + SOC_DOUBLE("Headphone Playback Switch",
> + SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
> + SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
> +};
> +
> +static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
> + /* Digital parts of the DACs */
> + SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
> + SUN4I_CODEC_DAC_DPC_EN_DA, 0,
> + NULL, 0),
> +
> + /* Analog parts of the DACs */
> + SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
> + SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
> +
> + /* Mixers */
> + SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
> + sun6i_codec_mixer_controls),
> + SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
> + SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
> + sun6i_codec_mixer_controls),
> +
> + /* Headphone output path */
> + SND_SOC_DAPM_MUX("Headphone Source Playback Route",
> + SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
> + SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
> + SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
> + SND_SOC_DAPM_OUTPUT("HP"),
> +};
> +
> +static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
> + /* DAC Routes */
> + { "Left DAC", NULL, "DAC Enable" },
> + { "Right DAC", NULL, "DAC Enable" },
> +
> + /* Left Mixer Routes */
> + { "Left Mixer", "DAC Playback Switch", "Left DAC" },
> + { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
> +
> + /* Right Mixer Routes */
> + { "Right Mixer", "DAC Playback Switch", "Right DAC" },
> + { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
> +
> + /* Headphone Routes */
> + { "Headphone Source Playback Route", "DAC", "Left DAC" },
> + { "Headphone Source Playback Route", "DAC", "Right DAC" },
> + { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
> + { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
> + { "Headphone Amp", NULL, "Headphone Source Playback Route" },
> + { "HP", NULL, "Headphone Amp" },
> +};
> +
> +static struct snd_soc_codec_driver sun6i_codec_codec = {
> + .component_driver = {
> + .controls = sun6i_codec_codec_widgets,
> + .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets),
> + .dapm_widgets = sun6i_codec_codec_dapm_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
> + .dapm_routes = sun6i_codec_codec_dapm_routes,
> + .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
> + },
> +};
> +
> static const struct snd_soc_component_driver sun4i_codec_component = {
> .name = "sun4i-codec",
> };
> @@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
> },
> };
>
> -static const struct regmap_config sun4i_codec_regmap_config = {
> - .reg_bits = 32,
> - .reg_stride = 4,
> - .val_bits = 32,
> - .max_register = SUN4I_CODEC_ADC_RXCNT,
> -};
> -
> -static const struct regmap_config sun7i_codec_regmap_config = {
> - .reg_bits = 32,
> - .reg_stride = 4,
> - .val_bits = 32,
> - .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
> -};
> -
> -struct sun4i_codec_quirks {
> - const struct regmap_config *regmap_config;
> -};
> -
> -static const struct sun4i_codec_quirks sun4i_codec_quirks = {
> - .regmap_config = &sun4i_codec_regmap_config,
> -};
> -
> -static const struct sun4i_codec_quirks sun7i_codec_quirks = {
> - .regmap_config = &sun7i_codec_regmap_config,
> -};
> -
> -static const struct of_device_id sun4i_codec_of_match[] = {
> - {
> - .compatible = "allwinner,sun4i-a10-codec",
> - .data = &sun4i_codec_quirks,
> - },
> - {
> - .compatible = "allwinner,sun7i-a20-codec",
> - .data = &sun7i_codec_quirks,
> - },
> - {}
> -};
> -MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> -
> static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
> int *num_links)
> {
> @@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
> return card;
> };
>
> +static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
> +{
> + struct snd_soc_card *card;
> +
> + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
> + if (!card)
> + return NULL;
> +
> + card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
> + if (!card->dai_link)
> + return NULL;
> +
> + card->dev = dev;
> + card->name = "sun4i-codec";
> +
> + return card;
> +};
> +
> +static const struct regmap_config sun4i_codec_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = SUN4I_CODEC_ADC_RXCNT,
> +};
> +
> +static const struct regmap_config sun6i_codec_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = SUN6I_CODEC_HMIC_DATA,
> +};
> +
> +static const struct regmap_config sun7i_codec_regmap_config = {
> + .reg_bits = 32,
> + .reg_stride = 4,
> + .val_bits = 32,
> + .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
> +};
> +
> +static const struct sun4i_codec_regs sun4i_codec_regs = {
> + .adc_fifoc = SUN4I_CODEC_ADC_FIFOC,
> + .adc_fifos = SUN4I_CODEC_ADC_FIFOS,
> + .adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
> +};
> +
> +static const struct sun4i_codec_regs sun6i_codec_regs = {
> + .adc_fifoc = SUN6I_CODEC_ADC_FIFOC,
> + .adc_fifos = SUN6I_CODEC_ADC_FIFOS,
> + .adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
> +};
> +
> +struct sun4i_codec_quirks {
> + const struct regmap_config *regmap_config;
> + const struct snd_soc_codec_driver *codec;
> + const struct sun4i_codec_regs *regs;
> + struct snd_soc_card * (*create_card)(struct device *dev);
> +};
> +
> +static const struct sun4i_codec_quirks sun4i_codec_quirks = {
> + .regmap_config = &sun4i_codec_regmap_config,
> + .regs = &sun4i_codec_regs,
> + .codec = &sun4i_codec_codec,
> + .create_card = sun4i_codec_create_card,
> +};
> +
> +static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
> + .regmap_config = &sun6i_codec_regmap_config,
> + .regs = &sun6i_codec_regs,
> + .codec = &sun6i_codec_codec,
> + .create_card = sun6i_codec_create_card,
> +};
> +
> +static const struct sun4i_codec_quirks sun7i_codec_quirks = {
> + .regmap_config = &sun7i_codec_regmap_config,
> + .regs = &sun4i_codec_regs,
> + .codec = &sun4i_codec_codec,
> + .create_card = sun4i_codec_create_card,
> +};
> +
> +static const struct of_device_id sun4i_codec_of_match[] = {
> + {
> + .compatible = "allwinner,sun4i-a10-codec",
> + .data = &sun4i_codec_quirks,
> + },
> + {
> + .compatible = "allwinner,sun6i-a31-codec",
> + .data = &sun6i_a31_codec_quirks,
> + },
> + {
> + .compatible = "allwinner,sun7i-a20-codec",
> + .data = &sun7i_codec_quirks,
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
> +
I don't really like moving blocks of code over and over again,
especially in the middle of an unrelated patch.
Thanks!
Maxime
--
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161003/b00e6c86/attachment.sig>
^ permalink raw reply
* Coresight ETF trace dump failed in Juno r1 with 4.8-rc8
From: Venkatesh Vivekanandan @ 2016-10-03 11:36 UTC (permalink / raw)
To: linux-arm-kernel
Hi All,
I am trying to collect ETF trace from Juno R1 and could see "cpu
stall" while dumping the trace. Attached is the log of sequence
followed. Was trying to collect the trace data from hardware and see
if it is any valid data. Am I missing anything here?.
cenaro-test [rc=0]# echo 0 > /sys/bus/coresight/devices/22140000.etm/enable_sour
[ 89.609083] coresight-etm4x 22140000.etm: ETM tracing disabled
[ 89.614892] coresight-funnel 220c0000.cluster0-funnel: FUNNEL
inport 1 disabled
[ 89.622140] coresight-funnel 20040000.main-funnel: FUNNEL inport 0 disabled
[ 89.629057] coresight-tmc 20010000.etf: TMC-ETB/ETF disabled
linaro-test [rc=0]# dd if=/dev/20010000.etf of=/cstrace.bin bs=1
[ 128.876846] INFO: rcu_preempt detected stalls on CPUs/tasks:
[ 128.882457] 3-...: (65 GPs behind) idle=a3f/140000000000000/0
softirq=314/315 fqs=2288
[ 128.890462] (detected by 1, t=5252 jiffies, g=0, c=-1, q=19)
[ 128.896151] Task dump for CPU 3:
[ 128.899340] dd R running task 0 1237 1234 0x00000002
[ 128.906322] Call trace:
[ 128.908745] [<ffff000008085560>] __switch_to+0xc8/0xd4
[ 128.913826] [<0000000000020000>] 0x20000
This happens consistently with every boot.
Thanks,
Venkatesh.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: r1-trace-log
Type: application/octet-stream
Size: 4121 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161003/34981cfd/attachment-0001.obj>
^ permalink raw reply
* [PATCH 2/2] drm/rockchip: analogix_dp: Refuse to enable PSR if panel doesn't support it
From: Archit Taneja @ 2016-10-03 11:13 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAOw6vbL1fDpPbFeePEFqYZ+i0sbbsB+vu7fCC8U88HAj-1D2Ag@mail.gmail.com>
On 09/27/2016 06:58 PM, Sean Paul wrote:
> On Fri, Sep 23, 2016 at 10:06 AM, Tomeu Vizoso
> <tomeu.vizoso@collabora.com> wrote:
>> There's no point in enabling PSR when the panel doesn't support it.
>>
>> This also avoids a problem when PSR gets enabled when a CRTC is being
>> disabled, because sometimes in that situation the DSP_HOLD_VALID_INTR
>> interrupt on which we wait will never arrive. This was observed on
>> RK3288 with a panel without PSR (veyron-jaq Chromebook).
>>
>> It's very easy to reproduce by running the kms_rmfb test in IGT a few
>> times.
>>
>> Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com>
>
> Thanks for digging into this.
>
> Reviewed-by: Sean Paul <seanpaul@chromium.org>
>
queued to drm-misc.
Archit
>
>
>> Cc: Sean Paul <seanpaul@chromium.org>
>> Cc: Yakir Yang <ykk@rock-chips.com>
>> Cc: Archit Taneja <architt@codeaurora.org>
>> ---
>> drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> index e83be157cc2a..8548e8271639 100644
>> --- a/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> +++ b/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
>> @@ -85,6 +85,9 @@ static void analogix_dp_psr_set(struct drm_encoder *encoder, bool enabled)
>> struct rockchip_dp_device *dp = to_dp(encoder);
>> unsigned long flags;
>>
>> + if (!analogix_dp_psr_supported(dp->dev))
>> + return;
>> +
>> dev_dbg(dp->dev, "%s PSR...\n", enabled ? "Entry" : "Exit");
>>
>> spin_lock_irqsave(&dp->psr_lock, flags);
>> --
>> 2.7.4
>>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH v5 2/5] drm/bridge: Add RGB to VGA bridge support
From: Archit Taneja @ 2016-10-03 11:10 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20160930143709.1388-3-maxime.ripard@free-electrons.com>
Hi Maxime,
On 09/30/2016 08:07 PM, Maxime Ripard wrote:
> Some boards have an entirely passive RGB to VGA bridge, based on either
> DACs or resistor ladders.
>
> Those might or might not have an i2c bus routed to the VGA connector in
> order to access the screen EDIDs.
>
> Add a bridge that doesn't do anything but expose the modes available on the
> screen, either based on the EDIDs if available, or based on the XGA
> standards.
>
> Acked-by: Rob Herring <robh@kernel.org>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> .../bindings/display/bridge/rgb-to-vga-bridge.txt | 48 +++++
> drivers/gpu/drm/bridge/Kconfig | 7 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/rgb-to-vga.c | 229 +++++++++++++++++++++
> 4 files changed, 285 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt
> create mode 100644 drivers/gpu/drm/bridge/rgb-to-vga.c
>
> diff --git a/Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt b/Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt
> new file mode 100644
> index 000000000000..a8375bc1f9cb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/bridge/rgb-to-vga-bridge.txt
> @@ -0,0 +1,48 @@
> +Dumb RGB to VGA bridge
> +----------------------
> +
> +This binding is aimed for dumb RGB to VGA bridges that do not require
> +any configuration.
> +
> +Required properties:
> +
> +- compatible: Must be "rgb-to-vga-bridge"
I'd talked to Laurent on IRC if he's okay with this. And I guess you to
had discussed it during XDC too. He's suggested that it'd be better to
have the compatible string as "simple-vga-dac".
Some of the reasons behind having this:
- We don't need to specify "rgb" in the compatible string since most
simple VGA DACs can only work with an RGB input.
- Also, with "dac" specified in the string, we don't need to
specifically mention "bridge" in the string. Also, bridge is a drm
specific term.
- "simple" is considered because it's an unconfigurable bridge, and it
might be misleading for other VGA DACs to not use "vga-dac".
What do you think about this? If you think it's good, would it be
possible for you to change this? I guess it's okay for the rest of
the patch to stay the same.
Sorry about the churn.
Thanks,
Archit
> +
> +Required nodes:
> +
> +This device has two video ports. Their connections are modeled using the OF
> +graph bindings specified in Documentation/devicetree/bindings/graph.txt.
> +
> +- Video port 0 for RGB input
> +- Video port 1 for VGA output
> +
> +
> +Example
> +-------
> +
> +bridge {
> + compatible = "rgb-to-vga-bridge";
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port at 0 {
> + reg = <0>;
> +
> + vga_bridge_in: endpoint {
> + remote-endpoint = <&tcon0_out_vga>;
> + };
> + };
> +
> + port at 1 {
> + reg = <1>;
> +
> + vga_bridge_out: endpoint {
> + remote-endpoint = <&vga_con_in>;
> + };
> + };
> + };
> +};
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index b590e678052d..d690398c541c 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -17,6 +17,13 @@ config DRM_ANALOGIX_ANX78XX
> the HDMI output of an application processor to MyDP
> or DisplayPort.
>
> +config DRM_RGB_TO_VGA
> + tristate "Dumb RGB to VGA Bridge support"
> + depends on OF
> + select DRM_KMS_HELPER
> + help
> + Support for passive RGB to VGA bridges
> +
> config DRM_DW_HDMI
> tristate
> select DRM_KMS_HELPER
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index efdb07e878f5..3bb8cbe09fe9 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -1,6 +1,7 @@
> ccflags-y := -Iinclude/drm
>
> obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o
> +obj-$(CONFIG_DRM_RGB_TO_VGA) += rgb-to-vga.o
> obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o
> obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
> obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> diff --git a/drivers/gpu/drm/bridge/rgb-to-vga.c b/drivers/gpu/drm/bridge/rgb-to-vga.c
> new file mode 100644
> index 000000000000..5ff4d4f3598f
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/rgb-to-vga.c
> @@ -0,0 +1,229 @@
> +/*
> + * Copyright (C) 2015-2016 Free Electrons
> + * Copyright (C) 2015-2016 NextThing Co
> + *
> + * Maxime Ripard <maxime.ripard@free-electrons.com>
> + *
> + * 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/of_graph.h>
> +
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +
> +struct dumb_vga {
> + struct drm_bridge bridge;
> + struct drm_connector connector;
> +
> + struct i2c_adapter *ddc;
> +};
> +
> +static inline struct dumb_vga *
> +drm_bridge_to_dumb_vga(struct drm_bridge *bridge)
> +{
> + return container_of(bridge, struct dumb_vga, bridge);
> +}
> +
> +static inline struct dumb_vga *
> +drm_connector_to_dumb_vga(struct drm_connector *connector)
> +{
> + return container_of(connector, struct dumb_vga, connector);
> +}
> +
> +static int dumb_vga_get_modes(struct drm_connector *connector)
> +{
> + struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
> + struct edid *edid;
> + int ret;
> +
> + if (IS_ERR(vga->ddc))
> + goto fallback;
> +
> + edid = drm_get_edid(connector, vga->ddc);
> + if (!edid) {
> + DRM_INFO("EDID readout failed, falling back to standard modes\n");
> + goto fallback;
> + }
> +
> + drm_mode_connector_update_edid_property(connector, edid);
> + return drm_add_edid_modes(connector, edid);
> +
> +fallback:
> + /*
> + * In case we cannot retrieve the EDIDs (broken or missing i2c
> + * bus), fallback on the XGA standards
> + */
> + ret = drm_add_modes_noedid(connector, 1920, 1200);
> +
> + /* And prefer a mode pretty much anyone can handle */
> + drm_set_preferred_mode(connector, 1024, 768);
> +
> + return ret;
> +}
> +
> +static const struct drm_connector_helper_funcs dumb_vga_con_helper_funcs = {
> + .get_modes = dumb_vga_get_modes,
> +};
> +
> +static enum drm_connector_status
> +dumb_vga_connector_detect(struct drm_connector *connector, bool force)
> +{
> + struct dumb_vga *vga = drm_connector_to_dumb_vga(connector);
> +
> + /*
> + * Even if we have an I2C bus, we can't assume that the cable
> + * is disconnected if drm_probe_ddc fails. Some cables don't
> + * wire the DDC pins, or the I2C bus might not be working at
> + * all.
> + */
> + if (!IS_ERR(vga->ddc) && drm_probe_ddc(vga->ddc))
> + return connector_status_connected;
> +
> + return connector_status_unknown;
> +}
> +
> +static void
> +dumb_vga_connector_destroy(struct drm_connector *connector)
> +{
> + drm_connector_cleanup(connector);
> +}
> +
> +static const struct drm_connector_funcs dumb_vga_con_funcs = {
> + .dpms = drm_atomic_helper_connector_dpms,
> + .detect = dumb_vga_connector_detect,
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .destroy = dumb_vga_connector_destroy,
> + .reset = drm_atomic_helper_connector_reset,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int dumb_vga_attach(struct drm_bridge *bridge)
> +{
> + struct dumb_vga *vga = drm_bridge_to_dumb_vga(bridge);
> + int ret;
> +
> + if (!bridge->encoder) {
> + DRM_ERROR("Missing encoder\n");
> + return -ENODEV;
> + }
> +
> + drm_connector_helper_add(&vga->connector,
> + &dumb_vga_con_helper_funcs);
> + ret = drm_connector_init(bridge->dev, &vga->connector,
> + &dumb_vga_con_funcs, DRM_MODE_CONNECTOR_VGA);
> + if (ret) {
> + DRM_ERROR("Failed to initialize connector\n");
> + return ret;
> + }
> +
> + drm_mode_connector_attach_encoder(&vga->connector,
> + bridge->encoder);
> +
> + return 0;
> +}
> +
> +static const struct drm_bridge_funcs dumb_vga_bridge_funcs = {
> + .attach = dumb_vga_attach,
> +};
> +
> +static struct i2c_adapter *dumb_vga_retrieve_ddc(struct device *dev)
> +{
> + struct device_node *end_node, *phandle, *remote;
> + struct i2c_adapter *ddc;
> +
> + end_node = of_graph_get_endpoint_by_regs(dev->of_node, 1, -1);
> + if (!end_node) {
> + dev_err(dev, "Missing connector endpoint\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + remote = of_graph_get_remote_port_parent(end_node);
> + of_node_put(end_node);
> + if (!remote) {
> + dev_err(dev, "Enable to parse remote node\n");
> + return ERR_PTR(-EINVAL);
> + }
> +
> + phandle = of_parse_phandle(remote, "ddc-i2c-bus", 0);
> + of_node_put(remote);
> + if (!phandle)
> + return ERR_PTR(-ENODEV);
> +
> + ddc = of_get_i2c_adapter_by_node(phandle);
> + of_node_put(phandle);
> + if (!ddc)
> + return ERR_PTR(-EPROBE_DEFER);
> +
> + return ddc;
> +}
> +
> +static int dumb_vga_probe(struct platform_device *pdev)
> +{
> + struct dumb_vga *vga;
> + int ret;
> +
> + vga = devm_kzalloc(&pdev->dev, sizeof(*vga), GFP_KERNEL);
> + if (!vga)
> + return -ENOMEM;
> + platform_set_drvdata(pdev, vga);
> +
> + vga->ddc = dumb_vga_retrieve_ddc(&pdev->dev);
> + if (IS_ERR(vga->ddc)) {
> + if (PTR_ERR(vga->ddc) == -ENODEV) {
> + dev_info(&pdev->dev,
> + "No i2c bus specified... Disabling EDID readout\n");
> + } else {
> + dev_err(&pdev->dev, "Couldn't retrieve i2c bus\n");
> + return PTR_ERR(vga->ddc);
> + }
> + }
> +
> + vga->bridge.funcs = &dumb_vga_bridge_funcs;
> + vga->bridge.of_node = pdev->dev.of_node;
> +
> + ret = drm_bridge_add(&vga->bridge);
> + if (ret && !IS_ERR(vga->ddc))
> + i2c_put_adapter(vga->ddc);
> +
> + return ret;
> +}
> +
> +static int dumb_vga_remove(struct platform_device *pdev)
> +{
> + struct dumb_vga *vga = platform_get_drvdata(pdev);
> +
> + drm_bridge_remove(&vga->bridge);
> +
> + if (!IS_ERR(vga->ddc))
> + i2c_put_adapter(vga->ddc);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id dumb_vga_match[] = {
> + { .compatible = "rgb-to-vga-bridge" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, dumb_vga_match);
> +
> +struct platform_driver dumb_vga_driver = {
> + .probe = dumb_vga_probe,
> + .remove = dumb_vga_remove,
> + .driver = {
> + .name = "rgb-to-vga-bridge",
> + .of_match_table = dumb_vga_match,
> + },
> +};
> +module_platform_driver(dumb_vga_driver);
> +
> +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
> +MODULE_DESCRIPTION("Dumb RGB to VGA bridge driver");
> +MODULE_LICENSE("GPL");
>
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
^ permalink raw reply
* [PATCH 12/12] ARM: dts: sun6i: hummingbird: Enable internal audio codec
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The Hummingbird A31 has headset and line in audio jacks and an onboard
mic routed to the pins for the SoC's internal codec. The line out pins
are routed to an onboard speaker amp, whose output is available on a
pin header.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun6i-a31-hummingbird.dts | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
index 9a74637f677f..67f46ea279a8 100644
--- a/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
+++ b/arch/arm/boot/dts/sun6i-a31-hummingbird.dts
@@ -69,6 +69,18 @@
};
};
+&codec {
+ allwinner,audio-routing =
+ "Headphone", "HP",
+ "Speaker", "LINEOUT",
+ "LINEIN", "Line In",
+ "MIC1", "Mic",
+ "MIC2", "Headset Mic",
+ "Mic", "MBIAS",
+ "Headset Mic", "HBIAS";
+ status = "okay";
+};
+
&cpu0 {
cpu-supply = <®_dcdc3>;
};
--
2.9.3
^ permalink raw reply related
* [PATCH 11/12] ARM: dts: sun6i: Add audio codec device node
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31 SoC includes the Allwinner audio codec, capable of 24-bit
playback up to 192 kHz and 24-bit capture up to 48 kHz.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
arch/arm/boot/dts/sun6i-a31.dtsi | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index ce1960453a0b..9024171d3aa8 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -728,6 +728,18 @@
reset-names = "ahb";
};
+ codec: codec at 01c22c00 {
+ #sound-dai-cells = <0>;
+ compatible = "allwinner,sun6i-a31-codec";
+ reg = <0x01c22c00 0x98>;
+ interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&ccu CLK_APB1_CODEC>, <&ccu CLK_CODEC>;
+ clock-names = "apb", "codec";
+ dmas = <&dma 15>, <&dma 15>;
+ dma-names = "rx", "tx";
+ status = "disabled";
+ };
+
timer at 01c60000 {
compatible = "allwinner,sun6i-a31-hstimer",
"allwinner,sun7i-a20-hstimer";
--
2.9.3
^ permalink raw reply related
* [PATCH 10/12] ASoC: sun4i-codec: Add support for A31 board level audio routing
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31 SoC's codec has various inputs, outputs and microphone bias
supplies. These can be routed on the board in different ways, such as:
- Microphones all use the MBIAS main microphone supply or one mic may
use the HBIAS supply, which supports headset detection and buttons.
- Line Out may be routed to an audio jack, or an onboard speaker amp
with power controls.
Add support for specifying the audio routes in the device tree.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
.../devicetree/bindings/sound/sun4i-codec.txt | 35 ++++++++++++++++++++++
sound/soc/sunxi/sun4i-codec.c | 21 +++++++++++--
2 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
index 1d2411cea98d..893f37413943 100644
--- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
+++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
@@ -19,6 +19,32 @@ Required properties:
Optional properties:
- allwinner,pa-gpios: gpio to enable external amplifier
+Required properties for "allwinner,sun6i-a31-codec":
+- allwinner,audio-routing: A list of the connections between audio components.
+ Each entry is a pair of strings, the first being the
+ connection's sink, the second being the connection's
+ source. Valid names include:
+
+ Audio pins on the SoC:
+ "HP"
+ "LINEIN"
+ "LINEOUT"
+ "MIC1"
+ "MIC2"
+ "MIC3"
+
+ Microphone biases from the SoC:
+ "HBIAS"
+ "MBIAS"
+
+ Board connectors:
+ "Headphone"
+ "Headset Mic"
+ "Line In"
+ "Line Out"
+ "Mic"
+ "Speaker"
+
Example:
codec: codec at 01c22c00 {
#sound-dai-cells = <0>;
@@ -29,4 +55,13 @@ codec: codec at 01c22c00 {
clock-names = "apb", "codec";
dmas = <&dma 0 19>, <&dma 0 19>;
dma-names = "rx", "tx";
+ allwinner,pa-gpios = <&pio 7 22 GPIO_ACTIVE_HIGH>; /* PH22 */
+ allwinner,audio-routing =
+ "Headphone", "HP",
+ "Speaker", "LINEOUT",
+ "LINEIN", "Line In",
+ "MIC1", "MBIAS",
+ "MIC1", "Mic",
+ "MIC2", "HBIAS",
+ "MIC2", "Headset Mic";
};
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index cd21914fd01f..4c364e6410dd 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -1133,9 +1133,19 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
return card;
};
+static const struct snd_soc_dapm_widget sun6i_codec_card_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_LINE("Line In", NULL),
+ SND_SOC_DAPM_LINE("Line Out", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", sun4i_codec_spk_event),
+};
+
static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
{
struct snd_soc_card *card;
+ int ret;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
@@ -1145,8 +1155,15 @@ static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
if (!card->dai_link)
return NULL;
- card->dev = dev;
- card->name = "sun4i-codec";
+ card->dev = dev;
+ card->name = "sun4i-codec";
+ card->dapm_widgets = sun6i_codec_card_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(sun6i_codec_card_dapm_widgets);
+ card->fully_routed = true;
+
+ ret = snd_soc_of_parse_audio_routing(card, "allwinner,audio-routing");
+ if (ret)
+ dev_warn(dev, "failed to parse audio-routing: %d\n", ret);
return card;
};
--
2.9.3
^ permalink raw reply related
* [PATCH 09/12] ASoC: sun4i-codec: Add support for A31 ADC capture path
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31's internal codec capture path has a mixer in front of the ADC
for each channel, capable of selecting various inputs, including
microphones, line in, phone in, and the main output mixer.
This patch adds the various controls, widgets and routes needed for
audio capture from the already supported inputs on the A31.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
sound/soc/sunxi/sun4i-codec.c | 65 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 727f54e3bd13..cd21914fd01f 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -761,6 +761,30 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
};
+/* ADC mixer controls */
+static const struct snd_kcontrol_new sun6i_codec_adc_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("Mixer Capture Switch",
+ SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL,
+ SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR, 1, 0),
+ SOC_DAPM_DOUBLE("Mixer Reversed Capture Switch",
+ SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR,
+ SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL, 1, 0),
+ SOC_DAPM_DOUBLE("Line In Capture Switch",
+ SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL,
+ SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR, 1, 0),
+ SOC_DAPM_DOUBLE("Mic1 Capture Switch",
+ SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1,
+ SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1, 1, 0),
+ SOC_DAPM_DOUBLE("Mic2 Capture Switch",
+ SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2,
+ SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2, 1, 0),
+};
+
/* headphone controls */
static const char * const sun6i_codec_hp_src_enum_text[] = {
"DAC", "Mixer",
@@ -856,6 +880,10 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
sun6i_codec_mic_gain_scale),
+ SOC_DOUBLE_TLV("ADC Capture Volume",
+ SUN6I_CODEC_ADC_ACTL, SUN6I_CODEC_ADC_ACTL_ADCLG,
+ SUN6I_CODEC_ADC_ACTL_ADCRG, 0x7, 0,
+ sun6i_codec_out_mixer_pregain_scale),
};
static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
@@ -881,6 +909,23 @@ static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
/* Line In */
SND_SOC_DAPM_INPUT("LINEIN"),
+ /* Digital parts of the ADCs */
+ SND_SOC_DAPM_SUPPLY("ADC Enable", SUN6I_CODEC_ADC_FIFOC,
+ SUN6I_CODEC_ADC_FIFOC_EN_AD, 0,
+ NULL, 0),
+
+ /* Analog parts of the ADCs */
+ SND_SOC_DAPM_ADC("Left ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_ADCLEN, 0),
+ SND_SOC_DAPM_ADC("Right ADC", "Codec Capture", SUN6I_CODEC_ADC_ACTL,
+ SUN6I_CODEC_ADC_ACTL_ADCREN, 0),
+
+ /* ADC Mixers */
+ SOC_MIXER_ARRAY("Left ADC Mixer", SND_SOC_NOPM, 0, 0,
+ sun6i_codec_adc_mixer_controls),
+ SOC_MIXER_ARRAY("Right ADC Mixer", SND_SOC_NOPM, 0, 0,
+ sun6i_codec_adc_mixer_controls),
+
/* Digital parts of the DACs */
SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
SUN4I_CODEC_DAC_DPC_EN_DA, 0,
@@ -938,6 +983,20 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
{ "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
+ /* Left ADC Mixer Routes */
+ { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
+ { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
+ { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
+ { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
+ { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
+ /* Right ADC Mixer Routes */
+ { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
+ { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
+ { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
+ { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
+ { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
/* Headphone Routes */
{ "Headphone Source Playback Route", "DAC", "Left DAC" },
{ "Headphone Source Playback Route", "DAC", "Right DAC" },
@@ -951,6 +1010,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
{ "Line Out Source Playback Route", "Stereo", "Right Mixer" },
{ "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
{ "LINEOUT", NULL, "Line Out Source Playback Route" },
+
+ /* ADC Routes */
+ { "Left ADC", NULL, "ADC Enable" },
+ { "Right ADC", NULL, "ADC Enable" },
+ { "Left ADC", NULL, "Left ADC Mixer" },
+ { "Right ADC", NULL, "Right ADC Mixer" },
};
static struct snd_soc_codec_driver sun6i_codec_codec = {
--
2.9.3
^ permalink raw reply related
* [PATCH 08/12] ASoC: sun4i-codec: Add support for A31 analog microphone inputs
From: Chen-Yu Tsai @ 2016-10-03 11:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31 internal codec has 3 microphone outputs, of which MIC2 and MIC3
are muxed internally. The resulting two microphone inputs have separate
gain controls and mixer inputs.
The codec also has 2 microphone bias pins. HBIAS is specifically for the
headphone jack, which also supports headphone detection and control
buttons. These extra functions are not supported yet. The other, MBIAS,
is for all other analog microphones.
There is also mention of digital microphone support, but documentation
is scarce, and no hardware with it is available.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
sound/soc/sunxi/sun4i-codec.c | 70 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 5cbbab62bfd2..727f54e3bd13 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -751,6 +751,14 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
SUN6I_CODEC_OM_DACA_CTRL,
SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
+ SOC_DAPM_DOUBLE("Mic1 Playback Switch",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1,
+ SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1, 1, 0),
+ SOC_DAPM_DOUBLE("Mic2 Playback Switch",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2,
+ SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2, 1, 0),
};
/* headphone controls */
@@ -768,6 +776,21 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
};
+/* microphone controls */
+static const char * const sun6i_codec_mic2_src_enum_text[] = {
+ "Mic2", "Mic3",
+};
+
+static SOC_ENUM_SINGLE_DECL(sun6i_codec_mic2_src_enum,
+ SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_MIC2SLT,
+ sun6i_codec_mic2_src_enum_text);
+
+static const struct snd_kcontrol_new sun6i_codec_mic2_src[] = {
+ SOC_DAPM_ENUM("Mic2 Amplifier Source Route",
+ sun6i_codec_mic2_src_enum),
+};
+
/* line out controls */
static const char * const sun6i_codec_lineout_src_enum_text[] = {
"Stereo", "Mono Differential",
@@ -790,6 +813,10 @@ static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
static const DECLARE_TLV_DB_SCALE(sun6i_codec_lineout_vol_scale, -4800, 150, 0);
static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
-450, 150, 0);
+static const DECLARE_TLV_DB_RANGE(sun6i_codec_mic_gain_scale,
+ 0, 0, TLV_DB_SCALE_ITEM(0, 0, 0),
+ 1, 7, TLV_DB_SCALE_ITEM(2400, 300, 0),
+);
static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
@@ -815,9 +842,42 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
SOC_SINGLE_TLV("Line In Playback Volume",
SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
0x7, 0, sun6i_codec_out_mixer_pregain_scale),
+ SOC_SINGLE_TLV("Mic1 Playback Volume",
+ SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC1G,
+ 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
+ SOC_SINGLE_TLV("Mic2 Playback Volume",
+ SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_MIC2G,
+ 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
+
+ /* Microphone Amp boost gains */
+ SOC_SINGLE_TLV("Mic1 Boost Volume", SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_MIC1BOOST, 0x7, 0,
+ sun6i_codec_mic_gain_scale),
+ SOC_SINGLE_TLV("Mic2 Boost Volume", SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_MIC2BOOST, 0x7, 0,
+ sun6i_codec_mic_gain_scale),
};
static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+ /* Microphone inputs */
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+
+ /* Microphone Bias */
+ SND_SOC_DAPM_SUPPLY("HBIAS", SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_HBIASEN, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MBIAS", SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_MBIASEN, 0, NULL, 0),
+
+ /* Mic input path */
+ SND_SOC_DAPM_MUX("Mic2 Amplifier Source Route",
+ SND_SOC_NOPM, 0, 0, sun6i_codec_mic2_src),
+ SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_MIC1AMPEN, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_MIC2AMPEN, 0, NULL, 0),
+
/* Line In */
SND_SOC_DAPM_INPUT("LINEIN"),
@@ -858,15 +918,25 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
{ "Left DAC", NULL, "DAC Enable" },
{ "Right DAC", NULL, "DAC Enable" },
+ /* Microphone Routes */
+ { "Mic1 Amplifier", NULL, "MIC1"},
+ { "Mic2 Amplifier Source Route", "Mic2", "MIC2" },
+ { "Mic2 Amplifier Source Route", "Mic3", "MIC3" },
+ { "Mic2 Amplifier", NULL, "Mic2 Amplifier Source Route"},
+
/* Left Mixer Routes */
{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
{ "Left Mixer", "Line In Playback Switch", "LINEIN" },
+ { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
+ { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
/* Right Mixer Routes */
{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
{ "Right Mixer", "Line In Playback Switch", "LINEIN" },
+ { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
+ { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
/* Headphone Routes */
{ "Headphone Source Playback Route", "DAC", "Left DAC" },
--
2.9.3
^ permalink raw reply related
* [PATCH 07/12] ASoC: sun4i-codec: Add support for A31 Line Out playback
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31 integrated codec has a second "Line Out" output which does not
include an integrated amplifier in its path. This path does have a
separate volume control.
This patch adds support for the playback path from the DAC to the Line
Out pins.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
sound/soc/sunxi/sun4i-codec.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index d9ec8215c1f9..5cbbab62bfd2 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -768,9 +768,26 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
};
+/* line out controls */
+static const char * const sun6i_codec_lineout_src_enum_text[] = {
+ "Stereo", "Mono Differential",
+};
+
+static SOC_ENUM_DOUBLE_DECL(sun6i_codec_lineout_src_enum,
+ SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC,
+ SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC,
+ sun6i_codec_lineout_src_enum_text);
+
+static const struct snd_kcontrol_new sun6i_codec_lineout_src[] = {
+ SOC_DAPM_ENUM("Line Out Source Playback Route",
+ sun6i_codec_lineout_src_enum),
+};
+
/* volume / mute controls */
static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_lineout_vol_scale, -4800, 150, 0);
static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
-450, 150, 0);
@@ -782,10 +799,18 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
SUN6I_CODEC_OM_DACA_CTRL,
SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
sun6i_codec_hp_vol_scale),
+ SOC_SINGLE_TLV("Line Out Playback Volume",
+ SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_LINEOUTVC, 0x1f, 0,
+ sun6i_codec_lineout_vol_scale),
SOC_DOUBLE("Headphone Playback Switch",
SUN6I_CODEC_OM_DACA_CTRL,
SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+ SOC_DOUBLE("Line Out Playback Switch",
+ SUN6I_CODEC_MIC_CTRL,
+ SUN6I_CODEC_MIC_CTRL_LINEOUTLEN,
+ SUN6I_CODEC_MIC_CTRL_LINEOUTREN, 1, 0),
/* Mixer pre-gains */
SOC_SINGLE_TLV("Line In Playback Volume",
SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
@@ -821,6 +846,11 @@ static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HP"),
+
+ /* Line Out path */
+ SND_SOC_DAPM_MUX("Line Out Source Playback Route",
+ SND_SOC_NOPM, 0, 0, sun6i_codec_lineout_src),
+ SND_SOC_DAPM_OUTPUT("LINEOUT"),
};
static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
@@ -845,6 +875,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
{ "Headphone Source Playback Route", "Mixer", "Right Mixer" },
{ "Headphone Amp", NULL, "Headphone Source Playback Route" },
{ "HP", NULL, "Headphone Amp" },
+
+ /* Line Out Routes */
+ { "Line Out Source Playback Route", "Stereo", "Left Mixer" },
+ { "Line Out Source Playback Route", "Stereo", "Right Mixer" },
+ { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" },
+ { "LINEOUT", NULL, "Line Out Source Playback Route" },
};
static struct snd_soc_codec_driver sun6i_codec_codec = {
--
2.9.3
^ permalink raw reply related
* [PATCH 06/12] ASoC: sun4i-codec: Add support for A31 Line In playback
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31 integrated codec has a stereo "Line In" input. Add support for
it to the playback paths.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
sound/soc/sunxi/sun4i-codec.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 9916714ecb71..d9ec8215c1f9 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -747,6 +747,10 @@ static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
SUN6I_CODEC_OM_DACA_CTRL,
SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
+ SOC_DAPM_DOUBLE("Line In Playback Switch",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL,
+ SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR, 1, 0),
};
/* headphone controls */
@@ -767,6 +771,8 @@ static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
/* volume / mute controls */
static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_out_mixer_pregain_scale,
+ -450, 150, 0);
static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
@@ -780,9 +786,16 @@ static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
SUN6I_CODEC_OM_DACA_CTRL,
SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+ /* Mixer pre-gains */
+ SOC_SINGLE_TLV("Line In Playback Volume",
+ SUN6I_CODEC_OM_PA_CTRL, SUN6I_CODEC_OM_PA_CTRL_LINEING,
+ 0x7, 0, sun6i_codec_out_mixer_pregain_scale),
};
static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+ /* Line In */
+ SND_SOC_DAPM_INPUT("LINEIN"),
+
/* Digital parts of the DACs */
SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
SUN4I_CODEC_DAC_DPC_EN_DA, 0,
@@ -818,10 +831,12 @@ static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
/* Left Mixer Routes */
{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
+ { "Left Mixer", "Line In Playback Switch", "LINEIN" },
/* Right Mixer Routes */
{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
+ { "Right Mixer", "Line In Playback Switch", "LINEIN" },
/* Headphone Routes */
{ "Headphone Source Playback Route", "DAC", "Left DAC" },
--
2.9.3
^ permalink raw reply related
* [PATCH 05/12] ASoC: sun4i-codec: Add support for A31 playback through headphone output
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
The A31 has a similar codec to the A10/A20. The PCM parts are very
similar, with just different register offsets. The analog paths are
very different. There are more inputs and outputs.
The quirks structure is expanded to include different register offsets
and separate callbacks for creating the ASoC card. Also the DMA burst
length is increased to 8. While the A10 DMA engine supports bursts of
1, 4 and 8, the A31 engine only supports 1 and 8.
This patch adds support for the basic playback path of the A31 codec,
from the DAC to the headphones. Headphone detection, microphone,
signaling, other inputs/outputs and capture will be added later.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
.../devicetree/bindings/sound/sun4i-codec.txt | 6 +-
sound/soc/sunxi/sun4i-codec.c | 396 +++++++++++++++++----
2 files changed, 334 insertions(+), 68 deletions(-)
diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
index 0dce690f78f5..1d2411cea98d 100644
--- a/Documentation/devicetree/bindings/sound/sun4i-codec.txt
+++ b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
@@ -1,8 +1,10 @@
* Allwinner A10 Codec
Required properties:
-- compatible: must be either "allwinner,sun4i-a10-codec" or
- "allwinner,sun7i-a20-codec"
+- compatible: must be one of the following compatibles:
+ - "allwinner,sun4i-a10-codec"
+ - "allwinner,sun6i-a31-codec"
+ - "allwinner,sun7i-a20-codec"
- reg: must contain the registers location and length
- interrupts: must contain the codec interrupt
- dmas: DMA channels for tx and rx dma. See the DMA client binding,
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index e047ec06d538..9916714ecb71 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -3,6 +3,7 @@
* Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
* Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
* Copyright 2015 Adam Sampson <ats@offog.org>
+ * Copyright 2016 Chen-Yu Tsai <wens@csie.org>
*
* Based on the Allwinner SDK driver, released under the GPL.
*
@@ -24,8 +25,9 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/of_platform.h>
#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <linux/clk.h>
#include <linux/regmap.h>
#include <linux/gpio/consumer.h>
@@ -55,6 +57,8 @@
#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH (0)
#define SUN4I_CODEC_DAC_FIFOS (0x08)
#define SUN4I_CODEC_DAC_TXDATA (0x0c)
+
+/* Codec DAC side analog signal controls */
#define SUN4I_CODEC_DAC_ACTL (0x10)
#define SUN4I_CODEC_DAC_ACTL_DACAENR (31)
#define SUN4I_CODEC_DAC_ACTL_DACAENL (30)
@@ -81,6 +85,8 @@
#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH (0)
#define SUN4I_CODEC_ADC_FIFOS (0x20)
#define SUN4I_CODEC_ADC_RXDATA (0x24)
+
+/* Codec ADC side analog signal controls */
#define SUN4I_CODEC_ADC_ACTL (0x28)
#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN (31)
#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN (30)
@@ -93,18 +99,106 @@
#define SUN4I_CODEC_ADC_ACTL_DDE (3)
#define SUN4I_CODEC_ADC_DEBUG (0x2c)
-/* Other various ADC registers */
+/* FIFO counters */
#define SUN4I_CODEC_DAC_TXCNT (0x30)
#define SUN4I_CODEC_ADC_RXCNT (0x34)
+
+/* Other various ADC registers */
#define SUN7I_CODEC_AC_DAC_CAL (0x38)
#define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)
+/*** sun6i specific register offsets ***/
+#define SUN6I_CODEC_ADC_FIFOC (0x10)
+#define SUN6I_CODEC_ADC_FIFOC_EN_AD (28)
+#define SUN6I_CODEC_ADC_FIFOS (0x14)
+#define SUN6I_CODEC_ADC_RXDATA (0x18)
+#define SUN6I_CODEC_OM_DACA_CTRL (0x20)
+#define SUN6I_CODEC_OM_DACA_CTRL_DACAREN (31)
+#define SUN6I_CODEC_OM_DACA_CTRL_DACALEN (30)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIXEN (29)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIXEN (28)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC1 (23)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_MIC2 (22)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONE (21)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_PHONEP (20)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_LINEINR (19)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR (18)
+#define SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL (17)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC1 (16)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_MIC2 (15)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONE (14)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_PHONEN (13)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_LINEINL (12)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL (11)
+#define SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR (10)
+#define SUN6I_CODEC_OM_DACA_CTRL_RHPIS (9)
+#define SUN6I_CODEC_OM_DACA_CTRL_LHPIS (8)
+#define SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE (7)
+#define SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE (6)
+#define SUN6I_CODEC_OM_DACA_CTRL_HPVOL (0)
+#define SUN6I_CODEC_OM_PA_CTRL (0x24)
+#define SUN6I_CODEC_OM_PA_CTRL_HPPAEN (31)
+#define SUN6I_CODEC_OM_PA_CTRL_MIC1G (15)
+#define SUN6I_CODEC_OM_PA_CTRL_MIC2G (12)
+#define SUN6I_CODEC_OM_PA_CTRL_LINEING (9)
+#define SUN6I_CODEC_OM_PA_CTRL_PHONEG (6)
+#define SUN6I_CODEC_OM_PA_CTRL_PHONEPG (3)
+#define SUN6I_CODEC_OM_PA_CTRL_PHONENG (0)
+#define SUN6I_CODEC_MIC_CTRL (0x28)
+#define SUN6I_CODEC_MIC_CTRL_HBIASEN (31)
+#define SUN6I_CODEC_MIC_CTRL_MBIASEN (30)
+#define SUN6I_CODEC_MIC_CTRL_MIC1AMPEN (28)
+#define SUN6I_CODEC_MIC_CTRL_MIC1BOOST (25)
+#define SUN6I_CODEC_MIC_CTRL_MIC2AMPEN (24)
+#define SUN6I_CODEC_MIC_CTRL_MIC2BOOST (21)
+#define SUN6I_CODEC_MIC_CTRL_MIC2SLT (20)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTLEN (19)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTREN (18)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTLSRC (17)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTRSRC (16)
+#define SUN6I_CODEC_MIC_CTRL_LINEOUTVC (11)
+#define SUN6I_CODEC_MIC_CTRL_PHONEPREG (8)
+#define SUN6I_CODEC_ADC_ACTL (0x2c)
+#define SUN6I_CODEC_ADC_ACTL_ADCREN (31)
+#define SUN6I_CODEC_ADC_ACTL_ADCLEN (30)
+#define SUN6I_CODEC_ADC_ACTL_ADCRG (27)
+#define SUN6I_CODEC_ADC_ACTL_ADCLG (24)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC1 (13)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_MIC2 (12)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONE (11)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_PHONEP (10)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_LINEINR (9)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXR (8)
+#define SUN6I_CODEC_ADC_ACTL_RADCMIX_OMIXL (7)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC1 (6)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_MIC2 (5)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONE (4)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_PHONEN (3)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_LINEINL (2)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXL (1)
+#define SUN6I_CODEC_ADC_ACTL_LADCMIX_OMIXR (0)
+#define SUN6I_CODEC_ADDA_TUNE (0x30)
+#define SUN6I_CODEC_CALIBRATION (0x34)
+#define SUN6I_CODEC_DAC_TXCNT (0x40)
+#define SUN6I_CODEC_ADC_RXCNT (0x44)
+#define SUN6I_CODEC_HMIC_CTL (0x50)
+#define SUN6I_CODEC_HMIC_DATA (0x54)
+
+/* TODO sun6i DAP (Digital Audio Processing) bits */
+
+struct sun4i_codec_regs {
+ u32 adc_fifoc;
+ u32 adc_fifos;
+ u32 adc_rxdata;
+};
+
struct sun4i_codec {
struct device *dev;
struct regmap *regmap;
struct clk *clk_apb;
struct clk *clk_module;
struct gpio_desc *gpio_pa;
+ const struct sun4i_codec_regs *regs;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
@@ -134,7 +228,7 @@ static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
{
/* Enable ADC DRQ */
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN),
BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN));
}
@@ -142,7 +236,7 @@ static void sun4i_codec_start_capture(struct sun4i_codec *scodec)
static void sun4i_codec_stop_capture(struct sun4i_codec *scodec)
{
/* Disable ADC DRQ */
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
BIT(SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN), 0);
}
@@ -186,13 +280,13 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
/* Flush RX FIFO */
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH),
BIT(SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH));
/* Set RX FIFO trigger level */
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
0xf << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL,
0x7 << SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL);
@@ -201,9 +295,12 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
* Allwinner's code mentions that it is related
* related to microphone gain
*/
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
- 0x3 << 25,
- 0x1 << 25);
+ if (!of_device_is_compatible(scodec->dev->of_node,
+ "allwinner,sun6i-a31-codec")) {
+ regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_ACTL,
+ 0x3 << 25,
+ 0x1 << 25);
+ }
if (of_device_is_compatible(scodec->dev->of_node,
"allwinner,sun7i-a20-codec"))
@@ -213,7 +310,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream,
0x1 << 8);
/* Fill most significant bits with valid data MSB */
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE),
BIT(SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE));
@@ -342,17 +439,17 @@ static int sun4i_codec_hw_params_capture(struct sun4i_codec *scodec,
unsigned int hwrate)
{
/* Set ADC sample rate */
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
7 << SUN4I_CODEC_ADC_FIFOC_ADC_FS,
hwrate << SUN4I_CODEC_ADC_FIFOC_ADC_FS);
/* Set the number of channels we want to use */
if (params_channels(params) == 1)
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN),
BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN));
else
- regmap_update_bits(scodec->regmap, SUN4I_CODEC_ADC_FIFOC,
+ regmap_update_bits(scodec->regmap, scodec->regs->adc_fifoc,
BIT(SUN4I_CODEC_ADC_FIFOC_MONO_EN), 0);
return 0;
@@ -385,7 +482,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
- /* Set TX FIFO mode to padding the LSBs with 0 */
+ /* Use higher 24 bits of TX FIFO */
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
0);
@@ -396,7 +493,7 @@ static int sun4i_codec_hw_params_playback(struct sun4i_codec *scodec,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
0);
- /* Set TX FIFO mode to repeat the MSB */
+ /* Use lower 16 bits of TX FIFO */
regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
@@ -502,7 +599,7 @@ static struct snd_soc_dai_driver sun4i_codec_dai = {
},
};
-/*** Codec ***/
+/*** sun4i Codec ***/
static const struct snd_kcontrol_new sun4i_codec_pa_mute =
SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
@@ -638,6 +735,114 @@ static struct snd_soc_codec_driver sun4i_codec_codec = {
},
};
+/*** sun6i Codec ***/
+
+/* mixer controls */
+static const struct snd_kcontrol_new sun6i_codec_mixer_controls[] = {
+ SOC_DAPM_DOUBLE("DAC Playback Switch",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACL,
+ SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACR, 1, 0),
+ SOC_DAPM_DOUBLE("DAC Reversed Playback Switch",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LMIX_DACR,
+ SUN6I_CODEC_OM_DACA_CTRL_RMIX_DACL, 1, 0),
+};
+
+/* headphone controls */
+static const char * const sun6i_codec_hp_src_enum_text[] = {
+ "DAC", "Mixer",
+};
+
+static SOC_ENUM_DOUBLE_DECL(sun6i_codec_hp_src_enum,
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LHPIS,
+ SUN6I_CODEC_OM_DACA_CTRL_RHPIS,
+ sun6i_codec_hp_src_enum_text);
+
+static const struct snd_kcontrol_new sun6i_codec_hp_src[] = {
+ SOC_DAPM_ENUM("Headphone Source Playback Route", sun6i_codec_hp_src_enum),
+};
+
+/* volume / mute controls */
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_dvol_scale, -7308, 116, 0);
+static const DECLARE_TLV_DB_SCALE(sun6i_codec_hp_vol_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun6i_codec_codec_widgets[] = {
+ SOC_SINGLE_TLV("DAC Playback Volume", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_DVOL, 0x3f, 1,
+ sun6i_codec_dvol_scale),
+ SOC_SINGLE_TLV("Headphone Playback Volume",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_HPVOL, 0x3f, 0,
+ sun6i_codec_hp_vol_scale),
+ SOC_DOUBLE("Headphone Playback Switch",
+ SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LHPPAMUTE,
+ SUN6I_CODEC_OM_DACA_CTRL_RHPPAMUTE, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun6i_codec_codec_dapm_widgets[] = {
+ /* Digital parts of the DACs */
+ SND_SOC_DAPM_SUPPLY("DAC Enable", SUN4I_CODEC_DAC_DPC,
+ SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+ NULL, 0),
+
+ /* Analog parts of the DACs */
+ SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_DACALEN, 0),
+ SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_DACAREN, 0),
+
+ /* Mixers */
+ SOC_MIXER_ARRAY("Left Mixer", SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_LMIXEN, 0,
+ sun6i_codec_mixer_controls),
+ SOC_MIXER_ARRAY("Right Mixer", SUN6I_CODEC_OM_DACA_CTRL,
+ SUN6I_CODEC_OM_DACA_CTRL_RMIXEN, 0,
+ sun6i_codec_mixer_controls),
+
+ /* Headphone output path */
+ SND_SOC_DAPM_MUX("Headphone Source Playback Route",
+ SND_SOC_NOPM, 0, 0, sun6i_codec_hp_src),
+ SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN6I_CODEC_OM_PA_CTRL,
+ SUN6I_CODEC_OM_PA_CTRL_HPPAEN, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("HP"),
+};
+
+static const struct snd_soc_dapm_route sun6i_codec_codec_dapm_routes[] = {
+ /* DAC Routes */
+ { "Left DAC", NULL, "DAC Enable" },
+ { "Right DAC", NULL, "DAC Enable" },
+
+ /* Left Mixer Routes */
+ { "Left Mixer", "DAC Playback Switch", "Left DAC" },
+ { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
+
+ /* Right Mixer Routes */
+ { "Right Mixer", "DAC Playback Switch", "Right DAC" },
+ { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
+
+ /* Headphone Routes */
+ { "Headphone Source Playback Route", "DAC", "Left DAC" },
+ { "Headphone Source Playback Route", "DAC", "Right DAC" },
+ { "Headphone Source Playback Route", "Mixer", "Left Mixer" },
+ { "Headphone Source Playback Route", "Mixer", "Right Mixer" },
+ { "Headphone Amp", NULL, "Headphone Source Playback Route" },
+ { "HP", NULL, "Headphone Amp" },
+};
+
+static struct snd_soc_codec_driver sun6i_codec_codec = {
+ .component_driver = {
+ .controls = sun6i_codec_codec_widgets,
+ .num_controls = ARRAY_SIZE(sun6i_codec_codec_widgets),
+ .dapm_widgets = sun6i_codec_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sun6i_codec_codec_dapm_widgets),
+ .dapm_routes = sun6i_codec_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sun6i_codec_codec_dapm_routes),
+ },
+};
+
static const struct snd_soc_component_driver sun4i_codec_component = {
.name = "sun4i-codec",
};
@@ -678,45 +883,6 @@ static struct snd_soc_dai_driver dummy_cpu_dai = {
},
};
-static const struct regmap_config sun4i_codec_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = SUN4I_CODEC_ADC_RXCNT,
-};
-
-static const struct regmap_config sun7i_codec_regmap_config = {
- .reg_bits = 32,
- .reg_stride = 4,
- .val_bits = 32,
- .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
-};
-
-struct sun4i_codec_quirks {
- const struct regmap_config *regmap_config;
-};
-
-static const struct sun4i_codec_quirks sun4i_codec_quirks = {
- .regmap_config = &sun4i_codec_regmap_config,
-};
-
-static const struct sun4i_codec_quirks sun7i_codec_quirks = {
- .regmap_config = &sun7i_codec_regmap_config,
-};
-
-static const struct of_device_id sun4i_codec_of_match[] = {
- {
- .compatible = "allwinner,sun4i-a10-codec",
- .data = &sun4i_codec_quirks,
- },
- {
- .compatible = "allwinner,sun7i-a20-codec",
- .data = &sun7i_codec_quirks,
- },
- {}
-};
-MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
-
static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
int *num_links)
{
@@ -781,6 +947,102 @@ static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
return card;
};
+static struct snd_soc_card *sun6i_codec_create_card(struct device *dev)
+{
+ struct snd_soc_card *card;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return NULL;
+
+ card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+ if (!card->dai_link)
+ return NULL;
+
+ card->dev = dev;
+ card->name = "sun4i-codec";
+
+ return card;
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN4I_CODEC_ADC_RXCNT,
+};
+
+static const struct regmap_config sun6i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN6I_CODEC_HMIC_DATA,
+};
+
+static const struct regmap_config sun7i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct sun4i_codec_regs sun4i_codec_regs = {
+ .adc_fifoc = SUN4I_CODEC_ADC_FIFOC,
+ .adc_fifos = SUN4I_CODEC_ADC_FIFOS,
+ .adc_rxdata = SUN4I_CODEC_ADC_RXDATA,
+};
+
+static const struct sun4i_codec_regs sun6i_codec_regs = {
+ .adc_fifoc = SUN6I_CODEC_ADC_FIFOC,
+ .adc_fifos = SUN6I_CODEC_ADC_FIFOS,
+ .adc_rxdata = SUN6I_CODEC_ADC_RXDATA,
+};
+
+struct sun4i_codec_quirks {
+ const struct regmap_config *regmap_config;
+ const struct snd_soc_codec_driver *codec;
+ const struct sun4i_codec_regs *regs;
+ struct snd_soc_card * (*create_card)(struct device *dev);
+};
+
+static const struct sun4i_codec_quirks sun4i_codec_quirks = {
+ .regmap_config = &sun4i_codec_regmap_config,
+ .regs = &sun4i_codec_regs,
+ .codec = &sun4i_codec_codec,
+ .create_card = sun4i_codec_create_card,
+};
+
+static const struct sun4i_codec_quirks sun6i_a31_codec_quirks = {
+ .regmap_config = &sun6i_codec_regmap_config,
+ .regs = &sun6i_codec_regs,
+ .codec = &sun6i_codec_codec,
+ .create_card = sun6i_codec_create_card,
+};
+
+static const struct sun4i_codec_quirks sun7i_codec_quirks = {
+ .regmap_config = &sun7i_codec_regmap_config,
+ .regs = &sun4i_codec_regs,
+ .codec = &sun4i_codec_codec,
+ .create_card = sun4i_codec_create_card,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+ {
+ .compatible = "allwinner,sun4i-a10-codec",
+ .data = &sun4i_codec_quirks,
+ },
+ {
+ .compatible = "allwinner,sun6i-a31-codec",
+ .data = &sun6i_a31_codec_quirks,
+ },
+ {
+ .compatible = "allwinner,sun7i-a20-codec",
+ .data = &sun7i_codec_quirks,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
static int sun4i_codec_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
@@ -790,11 +1052,18 @@ static int sun4i_codec_probe(struct platform_device *pdev)
void __iomem *base;
int ret;
+ quirks = of_device_get_match_data(&pdev->dev);
+ if (quirks == NULL) {
+ dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+ return -ENODEV;
+ }
+
scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
if (!scodec)
return -ENOMEM;
scodec->dev = &pdev->dev;
+ scodec->regs = quirks->regs;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
@@ -803,12 +1072,6 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return PTR_ERR(base);
}
- quirks = of_device_get_match_data(&pdev->dev);
- if (quirks == NULL) {
- dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
- return -ENODEV;
- }
-
scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
quirks->regmap_config);
if (IS_ERR(scodec->regmap)) {
@@ -846,15 +1109,15 @@ static int sun4i_codec_probe(struct platform_device *pdev)
/* DMA configuration for TX FIFO */
scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
- scodec->playback_dma_data.maxburst = 4;
+ scodec->playback_dma_data.maxburst = 8;
scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
/* DMA configuration for RX FIFO */
- scodec->capture_dma_data.addr = res->start + SUN4I_CODEC_ADC_RXDATA;
- scodec->capture_dma_data.maxburst = 4;
+ scodec->capture_dma_data.addr = res->start + quirks->regs->adc_rxdata;
+ scodec->capture_dma_data.maxburst = 8;
scodec->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
- ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+ ret = snd_soc_register_codec(&pdev->dev, quirks->codec,
&sun4i_codec_dai, 1);
if (ret) {
dev_err(&pdev->dev, "Failed to register our codec\n");
@@ -875,7 +1138,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
goto err_unregister_codec;
}
- card = sun4i_codec_create_card(&pdev->dev);
+ card = quirks->create_card(&pdev->dev);
if (!card) {
dev_err(&pdev->dev, "Failed to create our card\n");
goto err_unregister_codec;
@@ -925,4 +1188,5 @@ MODULE_DESCRIPTION("Allwinner A10 codec driver");
MODULE_AUTHOR("Emilio L?pez <emilio@elopez.com.ar>");
MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
MODULE_LICENSE("GPL");
--
2.9.3
^ permalink raw reply related
* [PATCH 04/12] ASoC: dapm: Introduce DAPM_DOUBLE_R dual channel dual register control type
From: Chen-Yu Tsai @ 2016-10-03 11:07 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161003110804.28235-1-wens@csie.org>
A DAPM_DOUBLE_R control type can be used for dual channel mixer input
selectors / mute controls across 2 registers, possibly toggling both
channels together.
The control is meant to be shared by 2 widgets, 1 for each channel,
such that the mixer control exposed to userspace remains a combined
stereo control.
Signed-off-by: Chen-Yu Tsai <wens@csie.org>
---
include/sound/soc-dapm.h | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index f74ec19687f8..a466f4bdc835 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -277,6 +277,11 @@ struct device;
.info = snd_soc_info_volsw, \
.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
.private_value = SOC_DOUBLE_VALUE(reg, lshift, rshift, max, invert, 0) }
+#define SOC_DAPM_DOUBLE_R(xname, lreg, rreg, shift, max, invert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+ .private_value = SOC_DOUBLE_R_VALUE(lreg, rreg, shift, max, invert) }
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \
--
2.9.3
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox