Commit 93ac820d authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/atmel' into asoc-next

parents daa5ab9e 3951e4aa
* Atmel SSC driver.
Required properties:
- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc"
- atmel,at91rm9200-ssc: support pdc transfer
- atmel,at91sam9g45-ssc: support dma transfer
- reg: Should contain SSC registers location and length
- interrupts: Should contain SSC interrupt
Example:
ssc0: ssc@fffbc000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfffbc000 0x4000>;
interrupts = <14 4 5>;
};
* Atmel at91sam9g20ek wm8731 audio complex
Required properties:
- compatible: "atmel,at91sam9g20ek-wm8731-audio"
- atmel,model: The user-visible name of this sound complex.
- atmel,audio-routing: A list of the connections between audio components.
- atmel,ssc-controller: The phandle of the SSC controller
- atmel,audio-codec: The phandle of the WM8731 audio codec
Optional properties:
- pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
Example:
sound {
compatible = "atmel,at91sam9g20ek-wm8731-audio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pck0_as_mck>;
atmel,model = "wm8731 @ AT91SAMG20EK";
atmel,audio-routing =
"Ext Spk", "LHPOUT",
"Int MIC", "MICIN";
atmel,ssc-controller = <&ssc0>;
atmel,audio-codec = <&wm8731>;
};
...@@ -29,6 +29,7 @@ aliases { ...@@ -29,6 +29,7 @@ aliases {
tcb0 = &tcb0; tcb0 = &tcb0;
tcb1 = &tcb1; tcb1 = &tcb1;
i2c0 = &i2c0; i2c0 = &i2c0;
ssc0 = &ssc0;
}; };
cpus { cpus {
cpu@0 { cpu@0 {
...@@ -212,6 +213,13 @@ i2c0: i2c@fffac000 { ...@@ -212,6 +213,13 @@ i2c0: i2c@fffac000 {
status = "disabled"; status = "disabled";
}; };
ssc0: ssc@fffbc000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfffbc000 0x4000>;
interrupts = <14 4 5>;
status = "disable";
};
adc0: adc@fffe0000 { adc0: adc@fffe0000 {
compatible = "atmel,at91sam9260-adc"; compatible = "atmel,at91sam9260-adc";
reg = <0xfffe0000 0x100>; reg = <0xfffe0000 0x100>;
......
...@@ -25,6 +25,8 @@ aliases { ...@@ -25,6 +25,8 @@ aliases {
gpio4 = &pioE; gpio4 = &pioE;
tcb0 = &tcb0; tcb0 = &tcb0;
i2c0 = &i2c0; i2c0 = &i2c0;
ssc0 = &ssc0;
ssc1 = &ssc1;
}; };
cpus { cpus {
cpu@0 { cpu@0 {
...@@ -173,6 +175,20 @@ usart2: serial@fff94000 { ...@@ -173,6 +175,20 @@ usart2: serial@fff94000 {
status = "disabled"; status = "disabled";
}; };
ssc0: ssc@fff98000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfff98000 0x4000>;
interrupts = <16 4 5>;
status = "disable";
};
ssc1: ssc@fff9c000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfff9c000 0x4000>;
interrupts = <17 4 5>;
status = "disable";
};
macb0: ethernet@fffbc000 { macb0: ethernet@fffbc000 {
compatible = "cdns,at32ap7000-macb", "cdns,macb"; compatible = "cdns,at32ap7000-macb", "cdns,macb";
reg = <0xfffbc000 0x100>; reg = <0xfffbc000 0x100>;
......
...@@ -30,6 +30,16 @@ main_clock: clock@0 { ...@@ -30,6 +30,16 @@ main_clock: clock@0 {
ahb { ahb {
apb { apb {
pinctrl@fffff400 {
board {
pinctrl_pck0_as_mck: pck0_as_mck {
atmel,pins =
<2 1 0x2 0x0>; /* PC1 periph B */
};
};
};
dbgu: serial@fffff200 { dbgu: serial@fffff200 {
status = "okay"; status = "okay";
}; };
...@@ -51,6 +61,11 @@ usb1: gadget@fffa4000 { ...@@ -51,6 +61,11 @@ usb1: gadget@fffa4000 {
atmel,vbus-gpio = <&pioC 5 0>; atmel,vbus-gpio = <&pioC 5 0>;
status = "okay"; status = "okay";
}; };
ssc0: ssc@fffbc000 {
status = "okay";
pinctrl-0 = <&pinctrl_ssc0_tx>;
};
}; };
nand0: nand@40000000 { nand0: nand@40000000 {
...@@ -114,7 +129,7 @@ i2c@0 { ...@@ -114,7 +129,7 @@ i2c@0 {
reg = <0x50>; reg = <0x50>;
}; };
wm8731@1b { wm8731: wm8731@1b {
compatible = "wm8731"; compatible = "wm8731";
reg = <0x1b>; reg = <0x1b>;
}; };
...@@ -139,4 +154,19 @@ btn4 { ...@@ -139,4 +154,19 @@ btn4 {
gpio-key,wakeup; gpio-key,wakeup;
}; };
}; };
sound {
compatible = "atmel,at91sam9g20ek-wm8731-audio";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pck0_as_mck>;
atmel,model = "wm8731 @ AT91SAMG20EK";
atmel,audio-routing =
"Ext Spk", "LHPOUT",
"Int Mic", "MICIN";
atmel,ssc-controller = <&ssc0>;
atmel,audio-codec = <&wm8731>;
};
}; };
...@@ -31,6 +31,8 @@ aliases { ...@@ -31,6 +31,8 @@ aliases {
tcb1 = &tcb1; tcb1 = &tcb1;
i2c0 = &i2c0; i2c0 = &i2c0;
i2c1 = &i2c1; i2c1 = &i2c1;
ssc0 = &ssc0;
ssc1 = &ssc1;
}; };
cpus { cpus {
cpu@0 { cpu@0 {
...@@ -226,6 +228,20 @@ i2c1: i2c@fff88000 { ...@@ -226,6 +228,20 @@ i2c1: i2c@fff88000 {
status = "disabled"; status = "disabled";
}; };
ssc0: ssc@fff9c000 {
compatible = "atmel,at91sam9g45-ssc";
reg = <0xfff9c000 0x4000>;
interrupts = <16 4 5>;
status = "disable";
};
ssc1: ssc@fffa0000 {
compatible = "atmel,at91sam9g45-ssc";
reg = <0xfffa0000 0x4000>;
interrupts = <17 4 5>;
status = "disable";
};
adc0: adc@fffb0000 { adc0: adc@fffb0000 {
compatible = "atmel,at91sam9260-adc"; compatible = "atmel,at91sam9260-adc";
reg = <0xfffb0000 0x100>; reg = <0xfffb0000 0x100>;
......
...@@ -30,6 +30,7 @@ aliases { ...@@ -30,6 +30,7 @@ aliases {
i2c0 = &i2c0; i2c0 = &i2c0;
i2c1 = &i2c1; i2c1 = &i2c1;
i2c2 = &i2c2; i2c2 = &i2c2;
ssc0 = &ssc0;
}; };
cpus { cpus {
cpu@0 { cpu@0 {
...@@ -87,6 +88,13 @@ pit: timer@fffffe30 { ...@@ -87,6 +88,13 @@ pit: timer@fffffe30 {
interrupts = <1 4 7>; interrupts = <1 4 7>;
}; };
ssc0: ssc@f0010000 {
compatible = "atmel,at91sam9g45-ssc";
reg = <0xf0010000 0x4000>;
interrupts = <28 4 5>;
status = "disable";
};
tcb0: timer@f8008000 { tcb0: timer@f8008000 {
compatible = "atmel,at91sam9x5-tcb"; compatible = "atmel,at91sam9x5-tcb";
reg = <0xf8008000 0x100>; reg = <0xf8008000 0x100>;
......
...@@ -184,9 +184,12 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -184,9 +184,12 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk),
CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
CLKDEV_CON_DEV_ID("pclk", "fffd0000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fffd4000.ssc", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fffd8000.ssc", &ssc2_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91rm9200.0", &twi_clk),
/* fake hclk clock */ /* fake hclk clock */
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &ohci_clk),
......
...@@ -752,7 +752,7 @@ static struct resource ssc0_resources[] = { ...@@ -752,7 +752,7 @@ static struct resource ssc0_resources[] = {
}; };
static struct platform_device at91rm9200_ssc0_device = { static struct platform_device at91rm9200_ssc0_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &ssc0_dmamask, .dma_mask = &ssc0_dmamask,
...@@ -794,7 +794,7 @@ static struct resource ssc1_resources[] = { ...@@ -794,7 +794,7 @@ static struct resource ssc1_resources[] = {
}; };
static struct platform_device at91rm9200_ssc1_device = { static struct platform_device at91rm9200_ssc1_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 1, .id = 1,
.dev = { .dev = {
.dma_mask = &ssc1_dmamask, .dma_mask = &ssc1_dmamask,
...@@ -836,7 +836,7 @@ static struct resource ssc2_resources[] = { ...@@ -836,7 +836,7 @@ static struct resource ssc2_resources[] = {
}; };
static struct platform_device at91rm9200_ssc2_device = { static struct platform_device at91rm9200_ssc2_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 2, .id = 2,
.dev = { .dev = {
.dma_mask = &ssc2_dmamask, .dma_mask = &ssc2_dmamask,
......
...@@ -210,7 +210,8 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -210,7 +210,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tc3_clk),
CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk), CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.1", &tc4_clk),
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk), CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.1", &tc5_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc_clk),
CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9260.0", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi_clk),
/* more usart lookup table for DT entries */ /* more usart lookup table for DT entries */
......
...@@ -742,7 +742,7 @@ static struct resource ssc_resources[] = { ...@@ -742,7 +742,7 @@ static struct resource ssc_resources[] = {
}; };
static struct platform_device at91sam9260_ssc_device = { static struct platform_device at91sam9260_ssc_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &ssc_dmamask, .dma_mask = &ssc_dmamask,
......
...@@ -174,9 +174,12 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -174,9 +174,12 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk), CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk),
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.2", &ssc2_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.2", &ssc2_clk),
CLKDEV_CON_DEV_ID("pclk", "fffbc000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc2_clk),
CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0), CLKDEV_CON_DEV_ID("hclk", "at91_ohci", &hck0),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9261.0", &twi_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi_clk),
......
...@@ -706,7 +706,7 @@ static struct resource ssc0_resources[] = { ...@@ -706,7 +706,7 @@ static struct resource ssc0_resources[] = {
}; };
static struct platform_device at91sam9261_ssc0_device = { static struct platform_device at91sam9261_ssc0_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &ssc0_dmamask, .dma_mask = &ssc0_dmamask,
...@@ -748,7 +748,7 @@ static struct resource ssc1_resources[] = { ...@@ -748,7 +748,7 @@ static struct resource ssc1_resources[] = {
}; };
static struct platform_device at91sam9261_ssc1_device = { static struct platform_device at91sam9261_ssc1_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 1, .id = 1,
.dev = { .dev = {
.dma_mask = &ssc1_dmamask, .dma_mask = &ssc1_dmamask,
...@@ -790,7 +790,7 @@ static struct resource ssc2_resources[] = { ...@@ -790,7 +790,7 @@ static struct resource ssc2_resources[] = {
}; };
static struct platform_device at91sam9261_ssc2_device = { static struct platform_device at91sam9261_ssc2_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 2, .id = 2,
.dev = { .dev = {
.dma_mask = &ssc2_dmamask, .dma_mask = &ssc2_dmamask,
......
...@@ -186,8 +186,10 @@ static struct clk *periph_clocks[] __initdata = { ...@@ -186,8 +186,10 @@ static struct clk *periph_clocks[] __initdata = {
static struct clk_lookup periph_clocks_lookups[] = { static struct clk_lookup periph_clocks_lookups[] = {
/* One additional fake clock for macb_hclk */ /* One additional fake clock for macb_hclk */
CLKDEV_CON_ID("hclk", &macb_clk), CLKDEV_CON_ID("hclk", &macb_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fff98000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc1_clk),
CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk), CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk), CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
......
...@@ -1199,7 +1199,7 @@ static struct resource ssc0_resources[] = { ...@@ -1199,7 +1199,7 @@ static struct resource ssc0_resources[] = {
}; };
static struct platform_device at91sam9263_ssc0_device = { static struct platform_device at91sam9263_ssc0_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &ssc0_dmamask, .dma_mask = &ssc0_dmamask,
...@@ -1241,7 +1241,7 @@ static struct resource ssc1_resources[] = { ...@@ -1241,7 +1241,7 @@ static struct resource ssc1_resources[] = {
}; };
static struct platform_device at91sam9263_ssc1_device = { static struct platform_device at91sam9263_ssc1_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 1, .id = 1,
.dev = { .dev = {
.dma_mask = &ssc1_dmamask, .dma_mask = &ssc1_dmamask,
......
...@@ -239,8 +239,10 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -239,8 +239,10 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.1", &tcb0_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g10.1", &twi1_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91sam9g45_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fff9c000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fffa0000.ssc", &ssc1_clk),
CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk), CLKDEV_CON_DEV_ID(NULL, "atmel-trng", &trng_clk),
CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_sha", &aestdessha_clk),
CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk), CLKDEV_CON_DEV_ID(NULL, "atmel_tdes", &aestdessha_clk),
......
...@@ -1459,7 +1459,7 @@ static struct resource ssc0_resources[] = { ...@@ -1459,7 +1459,7 @@ static struct resource ssc0_resources[] = {
}; };
static struct platform_device at91sam9g45_ssc0_device = { static struct platform_device at91sam9g45_ssc0_device = {
.name = "ssc", .name = "at91sam9g45_ssc",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &ssc0_dmamask, .dma_mask = &ssc0_dmamask,
...@@ -1501,7 +1501,7 @@ static struct resource ssc1_resources[] = { ...@@ -1501,7 +1501,7 @@ static struct resource ssc1_resources[] = {
}; };
static struct platform_device at91sam9g45_ssc1_device = { static struct platform_device at91sam9g45_ssc1_device = {
.name = "ssc", .name = "at91sam9g45_ssc",
.id = 1, .id = 1,
.dev = { .dev = {
.dma_mask = &ssc1_dmamask, .dma_mask = &ssc1_dmamask,
......
...@@ -184,8 +184,10 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -184,8 +184,10 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk), CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tc0_clk),
CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk), CLKDEV_CON_DEV_ID("t1_clk", "atmel_tcb.0", &tc1_clk),
CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk), CLKDEV_CON_DEV_ID("t2_clk", "atmel_tcb.0", &tc2_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk), CLKDEV_CON_DEV_ID("pclk", "at91rm9200_ssc.1", &ssc1_clk),
CLKDEV_CON_DEV_ID("pclk", "fffc0000.ssc", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "fffc4000.ssc", &ssc1_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.0", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "i2c-at91sam9g20.1", &twi1_clk),
CLKDEV_CON_ID("pioA", &pioA_clk), CLKDEV_CON_ID("pioA", &pioA_clk),
......
...@@ -832,7 +832,7 @@ static struct resource ssc0_resources[] = { ...@@ -832,7 +832,7 @@ static struct resource ssc0_resources[] = {
}; };
static struct platform_device at91sam9rl_ssc0_device = { static struct platform_device at91sam9rl_ssc0_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 0, .id = 0,
.dev = { .dev = {
.dma_mask = &ssc0_dmamask, .dma_mask = &ssc0_dmamask,
...@@ -874,7 +874,7 @@ static struct resource ssc1_resources[] = { ...@@ -874,7 +874,7 @@ static struct resource ssc1_resources[] = {
}; };
static struct platform_device at91sam9rl_ssc1_device = { static struct platform_device at91sam9rl_ssc1_device = {
.name = "ssc", .name = "at91rm9200_ssc",
.id = 1, .id = 1,
.dev = { .dev = {
.dma_mask = &ssc1_dmamask, .dma_mask = &ssc1_dmamask,
......
...@@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = { ...@@ -231,6 +231,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk), CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk), CLKDEV_CON_DEV_ID("dma_clk", "ffffec00.dma-controller", &dma0_clk),
CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk), CLKDEV_CON_DEV_ID("dma_clk", "ffffee00.dma-controller", &dma1_clk),
CLKDEV_CON_DEV_ID("pclk", "f0010000.ssc", &ssc_clk),
CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk), CLKDEV_CON_DEV_ID(NULL, "f8010000.i2c", &twi0_clk),
CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk), CLKDEV_CON_DEV_ID(NULL, "f8014000.i2c", &twi1_clk),
CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk), CLKDEV_CON_DEV_ID(NULL, "f8018000.i2c", &twi2_clk),
......
...@@ -353,6 +353,16 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = { ...@@ -353,6 +353,16 @@ static struct i2c_board_info __initdata ek_i2c_devices[] = {
}, },
}; };
static struct platform_device sam9g20ek_audio_device = {
.name = "at91sam9g20ek-audio",
.id = -1,
};
static void __init ek_add_device_audio(void)
{
platform_device_register(&sam9g20ek_audio_device);
}
static void __init ek_board_init(void) static void __init ek_board_init(void)
{ {
...@@ -394,6 +404,7 @@ static void __init ek_board_init(void) ...@@ -394,6 +404,7 @@ static void __init ek_board_init(void)
at91_set_B_periph(AT91_PIN_PC1, 0); at91_set_B_periph(AT91_PIN_PC1, 0);
/* SSC (for WM8731) */ /* SSC (for WM8731) */
at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX); at91_add_device_ssc(AT91SAM9260_ID_SSC, ATMEL_SSC_TX);
ek_add_device_audio();
} }
MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK") MACHINE_START(AT91SAM9G20EK, "Atmel AT91SAM9G20-EK")
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
/* Serialize access to ssc_list and user count */ /* Serialize access to ssc_list and user count */
static DEFINE_SPINLOCK(user_lock); static DEFINE_SPINLOCK(user_lock);
static LIST_HEAD(ssc_list); static LIST_HEAD(ssc_list);
...@@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num) ...@@ -29,7 +31,13 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
spin_lock(&user_lock); spin_lock(&user_lock);
list_for_each_entry(ssc, &ssc_list, list) { list_for_each_entry(ssc, &ssc_list, list) {
if (ssc->pdev->id == ssc_num) { if (ssc->pdev->dev.of_node) {
if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
== ssc_num) {
ssc_valid = 1;
break;
}
} else if (ssc->pdev->id == ssc_num) {
ssc_valid = 1; ssc_valid = 1;
break; break;
} }
...@@ -68,39 +76,93 @@ void ssc_free(struct ssc_device *ssc) ...@@ -68,39 +76,93 @@ void ssc_free(struct ssc_device *ssc)
} }
EXPORT_SYMBOL(ssc_free); EXPORT_SYMBOL(ssc_free);
static int __init ssc_probe(struct platform_device *pdev) static struct atmel_ssc_platform_data at91rm9200_config = {
.use_dma = 0,
};
static struct atmel_ssc_platform_data at91sam9g45_config = {
.use_dma = 1,
};
static const struct platform_device_id atmel_ssc_devtypes[] = {
{
.name = "at91rm9200_ssc",
.driver_data = (unsigned long) &at91rm9200_config,
}, {
.name = "at91sam9g45_ssc",
.driver_data = (unsigned long) &at91sam9g45_config,
}, {
/* sentinel */
}
};
#ifdef CONFIG_OF
static const struct of_device_id atmel_ssc_dt_ids[] = {
{
.compatible = "atmel,at91rm9200-ssc",
.data = &at91rm9200_config,
}, {
.compatible = "atmel,at91sam9g45-ssc",
.data = &at91sam9g45_config,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, atmel_ssc_dt_ids);
#endif
static inline const struct atmel_ssc_platform_data * __init
atmel_ssc_get_driver_data(struct platform_device *pdev)
{
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(atmel_ssc_dt_ids, pdev->dev.of_node);
if (match == NULL)
return NULL;
return match->data;
}
return (struct atmel_ssc_platform_data *)
platform_get_device_id(pdev)->driver_data;
}
static int ssc_probe(struct platform_device *pdev)
{ {
int retval = 0;
struct resource *regs; struct resource *regs;
struct ssc_device *ssc; struct ssc_device *ssc;
const struct atmel_ssc_platform_data *plat_dat;
ssc = kzalloc(sizeof(struct ssc_device), GFP_KERNEL); ssc = devm_kzalloc(&pdev->dev, sizeof(struct ssc_device), GFP_KERNEL);
if (!ssc) { if (!ssc) {
dev_dbg(&pdev->dev, "out of memory\n"); dev_dbg(&pdev->dev, "out of memory\n");
retval = -ENOMEM; return -ENOMEM;
goto out;
} }
ssc->pdev = pdev;
plat_dat = atmel_ssc_get_driver_data(pdev);
if (!plat_dat)
return -ENODEV;
ssc->pdata = (struct atmel_ssc_platform_data *)plat_dat;
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!regs) { if (!regs) {
dev_dbg(&pdev->dev, "no mmio resource defined\n"); dev_dbg(&pdev->dev, "no mmio resource defined\n");
retval = -ENXIO; return -ENXIO;
goto out_free;
} }
ssc->clk = clk_get(&pdev->dev, "pclk"); ssc->regs = devm_request_and_ioremap(&pdev->dev, regs);
if (IS_ERR(ssc->clk)) {
dev_dbg(&pdev->dev, "no pclk clock defined\n");
retval = -ENXIO;
goto out_free;
}
ssc->pdev = pdev;
ssc->regs = ioremap(regs->start, resource_size(regs));
if (!ssc->regs) { if (!ssc->regs) {
dev_dbg(&pdev->dev, "ioremap failed\n"); dev_dbg(&pdev->dev, "ioremap failed\n");
retval = -EINVAL; return -EINVAL;
goto out_clk; }
ssc->phybase = regs->start;
ssc->clk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(ssc->clk)) {
dev_dbg(&pdev->dev, "no pclk clock defined\n");
return -ENXIO;
} }
/* disable all interrupts */ /* disable all interrupts */
...@@ -112,8 +174,7 @@ static int __init ssc_probe(struct platform_device *pdev) ...@@ -112,8 +174,7 @@ static int __init ssc_probe(struct platform_device *pdev)
ssc->irq = platform_get_irq(pdev, 0); ssc->irq = platform_get_irq(pdev, 0);
if (!ssc->irq) { if (!ssc->irq) {
dev_dbg(&pdev->dev, "could not get irq\n"); dev_dbg(&pdev->dev, "could not get irq\n");
retval = -ENXIO; return -ENXIO;
goto out_unmap;
} }
spin_lock(&user_lock); spin_lock(&user_lock);
...@@ -125,16 +186,7 @@ static int __init ssc_probe(struct platform_device *pdev) ...@@ -125,16 +186,7 @@ static int __init ssc_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n", dev_info(&pdev->dev, "Atmel SSC device at 0x%p (irq %d)\n",
ssc->regs, ssc->irq); ssc->regs, ssc->irq);
goto out; return 0;
out_unmap:
iounmap(ssc->regs);
out_clk:
clk_put(ssc->clk);
out_free:
kfree(ssc);
out:
return retval;
} }
static int __devexit ssc_remove(struct platform_device *pdev) static int __devexit ssc_remove(struct platform_device *pdev)
...@@ -142,34 +194,23 @@ static int __devexit ssc_remove(struct platform_device *pdev) ...@@ -142,34 +194,23 @@ static int __devexit ssc_remove(struct platform_device *pdev)
struct ssc_device *ssc = platform_get_drvdata(pdev); struct ssc_device *ssc = platform_get_drvdata(pdev);
spin_lock(&user_lock); spin_lock(&user_lock);
iounmap(ssc->regs);
clk_put(ssc->clk);
list_del(&ssc->list); list_del(&ssc->list);
kfree(ssc);
spin_unlock(&user_lock); spin_unlock(&user_lock);
return 0; return 0;
} }
static struct platform_driver ssc_driver = { static struct platform_driver ssc_driver = {
.remove = __devexit_p(ssc_remove),
.driver = { .driver = {
.name = "ssc", .name = "ssc",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(atmel_ssc_dt_ids),
}, },
.id_table = atmel_ssc_devtypes,
.probe = ssc_probe,
.remove = __devexit_p(ssc_remove),
}; };
module_platform_driver(ssc_driver);
static int __init ssc_init(void)
{
return platform_driver_probe(&ssc_driver, ssc_probe);
}
module_init(ssc_init);
static void __exit ssc_exit(void)
{
platform_driver_unregister(&ssc_driver);
}
module_exit(ssc_exit);
MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>");
MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91"); MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91");
......
...@@ -5,10 +5,16 @@ ...@@ -5,10 +5,16 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/io.h> #include <linux/io.h>
struct atmel_ssc_platform_data {
int use_dma;
};
struct ssc_device { struct ssc_device {
struct list_head list; struct list_head list;
resource_size_t phybase;
void __iomem *regs; void __iomem *regs;
struct platform_device *pdev; struct platform_device *pdev;
struct atmel_ssc_platform_data *pdata;
struct clk *clk; struct clk *clk;
int user; int user;
int irq; int irq;
......
...@@ -6,6 +6,14 @@ config SND_ATMEL_SOC ...@@ -6,6 +6,14 @@ config SND_ATMEL_SOC
the ATMEL SSC interface. You will also need the ATMEL SSC interface. You will also need
to select the audio interfaces to support below. to select the audio interfaces to support below.
config SND_ATMEL_SOC_PDC
tristate
depends on SND_ATMEL_SOC
config SND_ATMEL_SOC_DMA
tristate
depends on SND_ATMEL_SOC
config SND_ATMEL_SOC_SSC config SND_ATMEL_SOC_SSC
tristate tristate
depends on SND_ATMEL_SOC depends on SND_ATMEL_SOC
...@@ -16,8 +24,8 @@ config SND_ATMEL_SOC_SSC ...@@ -16,8 +24,8 @@ config SND_ATMEL_SOC_SSC
config SND_AT91_SOC_SAM9G20_WM8731 config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
depends on ATMEL_SSC && ARCH_AT91SAM9G20 && SND_ATMEL_SOC && \ depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
AT91_PROGRAMMABLE_CLOCKS select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_SSC
select SND_SOC_WM8731 select SND_SOC_WM8731
help help
...@@ -27,6 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731 ...@@ -27,6 +35,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
config SND_AT91_SOC_AFEB9260 config SND_AT91_SOC_AFEB9260
tristate "SoC Audio support for AFEB9260 board" tristate "SoC Audio support for AFEB9260 board"
depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC select SND_ATMEL_SOC_SSC
select SND_SOC_TLV320AIC23 select SND_SOC_TLV320AIC23
help help
......
# AT91 Platform Support # AT91 Platform Support
snd-soc-atmel-pcm-objs := atmel-pcm.o snd-soc-atmel-pcm-objs := atmel-pcm.o
snd-soc-atmel-pcm-pdc-objs := atmel-pcm-pdc.o
snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o
snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o
obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o obj-$(CONFIG_SND_ATMEL_SOC) += snd-soc-atmel-pcm.o
obj-$(CONFIG_SND_ATMEL_SOC_PDC) += snd-soc-atmel-pcm-pdc.o
obj-$(CONFIG_SND_ATMEL_SOC_DMA) += snd-soc-atmel-pcm-dma.o
obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
# AT91 Machine Support # AT91 Machine Support
......
/*
* atmel-pcm-dma.c -- ALSA PCM DMA support for the Atmel SoC.
*
* Copyright (C) 2012 Atmel
*
* Author: Bo Shen <voice.shen@atmel.com>
*
* Based on atmel-pcm by:
* Sedji Gaouaou <sedji.gaouaou@atmel.com>
* Copyright 2008 Atmel
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/atmel-ssc.h>
#include <linux/platform_data/dma-atmel.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/dmaengine_pcm.h>
#include "atmel-pcm.h"
/*--------------------------------------------------------------------------*\
* Hardware definition
\*--------------------------------------------------------------------------*/
static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_PAUSE,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.period_bytes_min = 256, /* lighting DMA overhead */
.period_bytes_max = 2 * 0xffff, /* if 2 bytes format */
.periods_min = 8,
.periods_max = 1024, /* no limit */
.buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE,
};
/**
* atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
*
* We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
* check if any overrun occured.
*/
static void atmel_pcm_dma_irq(u32 ssc_sr,
struct snd_pcm_substream *substream)
{
struct atmel_pcm_dma_params *prtd;
prtd = snd_dmaengine_pcm_get_data(substream);
if (ssc_sr & prtd->mask->ssc_error) {
if (snd_pcm_running(substream))
pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n",
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
? "underrun" : "overrun", prtd->name,
ssc_sr);
/* stop RX and capture: will be enabled again at restart */
ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable);
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
/* now drain RHR and read status to remove xrun condition */
ssc_readx(prtd->ssc->regs, SSC_RHR);
ssc_readx(prtd->ssc->regs, SSC_SR);
}
}
/*--------------------------------------------------------------------------*\
* DMAENGINE operations
\*--------------------------------------------------------------------------*/
static bool filter(struct dma_chan *chan, void *slave)
{
struct at_dma_slave *sl = slave;
if (sl->dma_dev == chan->device->dev) {
chan->private = sl;
return true;
} else {
return false;
}
}
static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct atmel_pcm_dma_params *prtd;
struct ssc_device *ssc;
struct dma_chan *dma_chan;
struct dma_slave_config slave_config;
int ret;
prtd = snd_dmaengine_pcm_get_data(substream);
ssc = prtd->ssc;
ret = snd_hwparams_to_dma_slave_config(substream, params,
&slave_config);
if (ret) {
pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
return ret;
}
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR;
slave_config.dst_maxburst = 1;
} else {
slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR;
slave_config.src_maxburst = 1;
}
slave_config.device_fc = false;
dma_chan = snd_dmaengine_pcm_get_chan(substream);
if (dmaengine_slave_config(dma_chan, &slave_config)) {
pr_err("atmel-pcm: failed to configure dma channel\n");
ret = -EBUSY;
return ret;
}
return 0;
}
static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct atmel_pcm_dma_params *prtd;
struct ssc_device *ssc;
struct at_dma_slave *sdata = NULL;
int ret;
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
ssc = prtd->ssc;
if (ssc->pdev)
sdata = ssc->pdev->dev.platform_data;
ret = snd_dmaengine_pcm_open(substream, filter, sdata);
if (ret) {
pr_err("atmel-pcm: dmaengine pcm open failed\n");
return -EINVAL;
}
snd_dmaengine_pcm_set_data(substream, prtd);
ret = atmel_pcm_configure_dma(substream, params);
if (ret) {
pr_err("atmel-pcm: failed to configure dmai\n");
goto err;
}
prtd->dma_intr_handler = atmel_pcm_dma_irq;
return 0;
err:
snd_dmaengine_pcm_close(substream);
return ret;
}
static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream)
{
struct atmel_pcm_dma_params *prtd;
prtd = snd_dmaengine_pcm_get_data(substream);
ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error);
ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable);
return 0;
}
static int atmel_pcm_open(struct snd_pcm_substream *substream)
{
snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware);
return 0;
}
static int atmel_pcm_close(struct snd_pcm_substream *substream)
{
snd_dmaengine_pcm_close(substream);
return 0;
}
static struct snd_pcm_ops atmel_pcm_ops = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = atmel_pcm_hw_params,
.prepare = atmel_pcm_dma_prepare,
.trigger = snd_dmaengine_pcm_trigger,
.pointer = snd_dmaengine_pcm_pointer_no_residue,
.mmap = atmel_pcm_mmap,
};
static struct snd_soc_platform_driver atmel_soc_platform = {
.ops = &atmel_pcm_ops,
.pcm_new = atmel_pcm_new,
.pcm_free = atmel_pcm_free,
};
int atmel_pcm_dma_platform_register(struct device *dev)
{
return snd_soc_register_platform(dev, &atmel_soc_platform);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
void atmel_pcm_dma_platform_unregister(struct device *dev)
{
snd_soc_unregister_platform(dev);
}
EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
MODULE_DESCRIPTION("Atmel DMA based PCM module");
MODULE_LICENSE("GPL");
This diff is collapsed.
This diff is collapsed.
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
#include <linux/atmel-ssc.h> #include <linux/atmel-ssc.h>
#define ATMEL_SSC_DMABUF_SIZE (64 * 1024)
/* /*
* Registers and status bits that are required by the PCM driver. * Registers and status bits that are required by the PCM driver.
*/ */
...@@ -50,6 +52,7 @@ struct atmel_pdc_regs { ...@@ -50,6 +52,7 @@ struct atmel_pdc_regs {
struct atmel_ssc_mask { struct atmel_ssc_mask {
u32 ssc_enable; /* SSC recv/trans enable */ u32 ssc_enable; /* SSC recv/trans enable */
u32 ssc_disable; /* SSC recv/trans disable */ u32 ssc_disable; /* SSC recv/trans disable */
u32 ssc_error; /* SSC error conditions */
u32 ssc_endx; /* SSC ENDTX or ENDRX */ u32 ssc_endx; /* SSC ENDTX or ENDRX */
u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */ u32 ssc_endbuf; /* SSC TXBUFE or RXBUFF */
u32 pdc_enable; /* PDC recv/trans enable */ u32 pdc_enable; /* PDC recv/trans enable */
...@@ -80,4 +83,35 @@ struct atmel_pcm_dma_params { ...@@ -80,4 +83,35 @@ struct atmel_pcm_dma_params {
#define ssc_readx(base, reg) (__raw_readl((base) + (reg))) #define ssc_readx(base, reg) (__raw_readl((base) + (reg)))
#define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg)) #define ssc_writex(base, reg, value) __raw_writel((value), (base) + (reg))
int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd);
void atmel_pcm_free(struct snd_pcm *pcm);
int atmel_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma);
#ifdef CONFIG_SND_ATMEL_SOC_PDC
int atmel_pcm_pdc_platform_register(struct device *dev);
void atmel_pcm_pdc_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_pdc_platform_register(struct device *dev)
{
return 0;
}
static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
{
}
#endif
#ifdef CONFIG_SND_ATMEL_SOC_DMA
int atmel_pcm_dma_platform_register(struct device *dev);
void atmel_pcm_dma_platform_unregister(struct device *dev);
#else
static inline int atmel_pcm_dma_platform_register(struct device *dev)
{
return 0;
}
static inline void atmel_pcm_dma_platform_unregister(struct device *dev)
{
}
#endif
#endif /* _ATMEL_PCM_H */ #endif /* _ATMEL_PCM_H */
...@@ -48,11 +48,7 @@ ...@@ -48,11 +48,7 @@
#include "atmel_ssc_dai.h" #include "atmel_ssc_dai.h"
#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9G20)
#define NUM_SSC_DEVICES 1
#else
#define NUM_SSC_DEVICES 3 #define NUM_SSC_DEVICES 3
#endif
/* /*
* SSC PDC registers required by the PCM DMA engine. * SSC PDC registers required by the PCM DMA engine.
...@@ -107,7 +103,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { ...@@ -107,7 +103,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.pdc = &pdc_rx_reg, .pdc = &pdc_rx_reg,
.mask = &ssc_rx_mask, .mask = &ssc_rx_mask,
} }, } },
#if NUM_SSC_DEVICES == 3
{{ {{
.name = "SSC1 PCM out", .name = "SSC1 PCM out",
.pdc = &pdc_tx_reg, .pdc = &pdc_tx_reg,
...@@ -128,7 +123,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = { ...@@ -128,7 +123,6 @@ static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
.pdc = &pdc_rx_reg, .pdc = &pdc_rx_reg,
.mask = &ssc_rx_mask, .mask = &ssc_rx_mask,
} }, } },
#endif
}; };
...@@ -139,7 +133,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = { ...@@ -139,7 +133,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
.dir_mask = SSC_DIR_MASK_UNUSED, .dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0, .initialized = 0,
}, },
#if NUM_SSC_DEVICES == 3
{ {
.name = "ssc1", .name = "ssc1",
.lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock), .lock = __SPIN_LOCK_UNLOCKED(ssc_info[1].lock),
...@@ -152,7 +145,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = { ...@@ -152,7 +145,6 @@ static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
.dir_mask = SSC_DIR_MASK_UNUSED, .dir_mask = SSC_DIR_MASK_UNUSED,
.initialized = 0, .initialized = 0,
}, },
#endif
}; };
...@@ -690,27 +682,9 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) ...@@ -690,27 +682,9 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
static int atmel_ssc_probe(struct snd_soc_dai *dai) static int atmel_ssc_probe(struct snd_soc_dai *dai)
{ {
struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
int ret = 0;
snd_soc_dai_set_drvdata(dai, ssc_p); snd_soc_dai_set_drvdata(dai, ssc_p);
/*
* Request SSC device
*/
ssc_p->ssc = ssc_request(dai->id);
if (IS_ERR(ssc_p->ssc)) {
printk(KERN_ERR "ASoC: Failed to request SSC %d\n", dai->id);
ret = PTR_ERR(ssc_p->ssc);
}
return ret;
}
static int atmel_ssc_remove(struct snd_soc_dai *dai)
{
struct atmel_ssc_info *ssc_p = snd_soc_dai_get_drvdata(dai);
ssc_free(ssc_p->ssc);
return 0; return 0;
} }
...@@ -728,30 +702,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { ...@@ -728,30 +702,8 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.set_clkdiv = atmel_ssc_set_dai_clkdiv, .set_clkdiv = atmel_ssc_set_dai_clkdiv,
}; };
static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { static struct snd_soc_dai_driver atmel_ssc_dai = {
{
.name = "atmel-ssc-dai.0",
.probe = atmel_ssc_probe,
.remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
},
#if NUM_SSC_DEVICES == 3
{
.name = "atmel-ssc-dai.1",
.probe = atmel_ssc_probe, .probe = atmel_ssc_probe,
.remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend, .suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume, .resume = atmel_ssc_resume,
.playback = { .playback = {
...@@ -765,50 +717,50 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = { ...@@ -765,50 +717,50 @@ static struct snd_soc_dai_driver atmel_ssc_dai[NUM_SSC_DEVICES] = {
.rates = ATMEL_SSC_RATES, .rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,}, .formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops, .ops = &atmel_ssc_dai_ops,
},
{
.name = "atmel-ssc-dai.2",
.probe = atmel_ssc_probe,
.remove = atmel_ssc_remove,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = &atmel_ssc_dai_ops,
},
#endif
}; };
static __devinit int asoc_ssc_probe(struct platform_device *pdev) static int asoc_ssc_init(struct device *dev)
{ {
BUG_ON(pdev->id < 0); struct platform_device *pdev = to_platform_device(dev);
BUG_ON(pdev->id >= ARRAY_SIZE(atmel_ssc_dai)); struct ssc_device *ssc = platform_get_drvdata(pdev);
return snd_soc_register_dai(&pdev->dev, &atmel_ssc_dai[pdev->id]); int ret;
}
ret = snd_soc_register_dai(dev, &atmel_ssc_dai);
if (ret) {
dev_err(dev, "Could not register DAI: %d\n", ret);
goto err;
}
if (ssc->pdata->use_dma)
ret = atmel_pcm_dma_platform_register(dev);
else
ret = atmel_pcm_pdc_platform_register(dev);
if (ret) {
dev_err(dev, "Could not register PCM: %d\n", ret);
goto err_unregister_dai;
};
static int __devexit asoc_ssc_remove(struct platform_device *pdev)
{
snd_soc_unregister_dai(&pdev->dev);
return 0; return 0;
err_unregister_dai:
snd_soc_unregister_dai(dev);
err:
return ret;
} }
static struct platform_driver asoc_ssc_driver = { static void asoc_ssc_exit(struct device *dev)
.driver = { {
.name = "atmel-ssc-dai", struct platform_device *pdev = to_platform_device(dev);
.owner = THIS_MODULE, struct ssc_device *ssc = platform_get_drvdata(pdev);
},
.probe = asoc_ssc_probe, if (ssc->pdata->use_dma)
.remove = __devexit_p(asoc_ssc_remove), atmel_pcm_dma_platform_unregister(dev);
}; else
atmel_pcm_pdc_platform_unregister(dev);
snd_soc_unregister_dai(dev);
}
/** /**
* atmel_ssc_set_audio - Allocate the specified SSC for audio use. * atmel_ssc_set_audio - Allocate the specified SSC for audio use.
...@@ -816,50 +768,32 @@ static struct platform_driver asoc_ssc_driver = { ...@@ -816,50 +768,32 @@ static struct platform_driver asoc_ssc_driver = {
int atmel_ssc_set_audio(int ssc_id) int atmel_ssc_set_audio(int ssc_id)
{ {
struct ssc_device *ssc; struct ssc_device *ssc;
static struct platform_device *dma_pdev;
struct platform_device *ssc_pdev;
int ret; int ret;
if (ssc_id < 0 || ssc_id >= ARRAY_SIZE(atmel_ssc_dai))
return -EINVAL;
/* Allocate a dummy device for DMA if we don't have one already */
if (!dma_pdev) {
dma_pdev = platform_device_alloc("atmel-pcm-audio", -1);
if (!dma_pdev)
return -ENOMEM;
ret = platform_device_add(dma_pdev);
if (ret < 0) {
platform_device_put(dma_pdev);
dma_pdev = NULL;
return ret;
}
}
ssc_pdev = platform_device_alloc("atmel-ssc-dai", ssc_id);
if (!ssc_pdev)
return -ENOMEM;
/* If we can grab the SSC briefly to parent the DAI device off it */ /* If we can grab the SSC briefly to parent the DAI device off it */
ssc = ssc_request(ssc_id); ssc = ssc_request(ssc_id);
if (IS_ERR(ssc)) if (IS_ERR(ssc)) {
pr_warn("Unable to parent ASoC SSC DAI on SSC: %ld\n", pr_err("Unable to parent ASoC SSC DAI on SSC: %ld\n",
PTR_ERR(ssc)); PTR_ERR(ssc));
else { return PTR_ERR(ssc);
ssc_pdev->dev.parent = &(ssc->pdev->dev); } else {
ssc_free(ssc); ssc_info[ssc_id].ssc = ssc;
} }
ret = platform_device_add(ssc_pdev); ret = asoc_ssc_init(&ssc->pdev->dev);
if (ret < 0)
platform_device_put(ssc_pdev);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(atmel_ssc_set_audio); EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
module_platform_driver(asoc_ssc_driver); void atmel_ssc_put_audio(int ssc_id)
{
struct ssc_device *ssc = ssc_info[ssc_id].ssc;
ssc_free(ssc);
asoc_ssc_exit(&ssc->pdev->dev);
}
EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
/* Module information */ /* Module information */
MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com"); MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
......
...@@ -117,6 +117,7 @@ struct atmel_ssc_info { ...@@ -117,6 +117,7 @@ struct atmel_ssc_info {
struct atmel_ssc_state ssc_state; struct atmel_ssc_state ssc_state;
}; };
int atmel_ssc_set_audio(int ssc); int atmel_ssc_set_audio(int ssc_id);
void atmel_ssc_put_audio(int ssc_id);
#endif /* _AT91_SSC_DAI_H */ #endif /* _AT91_SSC_DAI_H */
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/pinctrl/consumer.h>
#include <linux/atmel-ssc.h> #include <linux/atmel-ssc.h>
#include <sound/core.h> #include <sound/core.h>
...@@ -179,10 +181,10 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd) ...@@ -179,10 +181,10 @@ static int at91sam9g20ek_wm8731_init(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_dai_link at91sam9g20ek_dai = { static struct snd_soc_dai_link at91sam9g20ek_dai = {
.name = "WM8731", .name = "WM8731",
.stream_name = "WM8731 PCM", .stream_name = "WM8731 PCM",
.cpu_dai_name = "atmel-ssc-dai.0", .cpu_dai_name = "at91rm9200_ssc.0",
.codec_dai_name = "wm8731-hifi", .codec_dai_name = "wm8731-hifi",
.init = at91sam9g20ek_wm8731_init, .init = at91sam9g20ek_wm8731_init,
.platform_name = "atmel-pcm-audio", .platform_name = "at91rm9200_ssc.0",
.codec_name = "wm8731.0-001b", .codec_name = "wm8731.0-001b",
.ops = &at91sam9g20ek_ops, .ops = &at91sam9g20ek_ops,
}; };
...@@ -195,20 +197,31 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = { ...@@ -195,20 +197,31 @@ static struct snd_soc_card snd_soc_at91sam9g20ek = {
.set_bias_level = at91sam9g20ek_set_bias_level, .set_bias_level = at91sam9g20ek_set_bias_level,
}; };
static struct platform_device *at91sam9g20ek_snd_device; static int __devinit at91sam9g20ek_audio_probe(struct platform_device *pdev)
static int __init at91sam9g20ek_init(void)
{ {
struct device_node *np = pdev->dev.of_node;
struct device_node *codec_np, *cpu_np;
struct clk *pllb; struct clk *pllb;
struct snd_soc_card *card = &snd_soc_at91sam9g20ek;
struct pinctrl *pinctrl;
int ret; int ret;
if (!(machine_is_at91sam9g20ek() || machine_is_at91sam9g20ek_2mmc())) pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
return -ENODEV; if (IS_ERR(pinctrl)) {
dev_err(&pdev->dev, "Failed to request pinctrl for mck\n");
return PTR_ERR(pinctrl);
}
if (!np) {
if (!(machine_is_at91sam9g20ek() ||
machine_is_at91sam9g20ek_2mmc()))
return -ENODEV;
}
ret = atmel_ssc_set_audio(0); ret = atmel_ssc_set_audio(0);
if (ret != 0) { if (ret) {
pr_err("Failed to set SSC 0 for audio: %d\n", ret); dev_err(&pdev->dev, "ssc channel is not valid\n");
return ret; return -EINVAL;
} }
/* /*
...@@ -236,45 +249,92 @@ static int __init at91sam9g20ek_init(void) ...@@ -236,45 +249,92 @@ static int __init at91sam9g20ek_init(void)
clk_set_rate(mclk, MCLK_RATE); clk_set_rate(mclk, MCLK_RATE);
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1); card->dev = &pdev->dev;
if (!at91sam9g20ek_snd_device) {
printk(KERN_ERR "ASoC: Platform device allocation failed\n"); /* Parse device node info */
ret = -ENOMEM; if (np) {
goto err_mclk; ret = snd_soc_of_parse_card_name(card, "atmel,model");
if (ret)
goto err;
ret = snd_soc_of_parse_audio_routing(card,
"atmel,audio-routing");
if (ret)
goto err;
/* Parse codec info */
at91sam9g20ek_dai.codec_name = NULL;
codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
if (!codec_np) {
dev_err(&pdev->dev, "codec info missing\n");
return -EINVAL;
}
at91sam9g20ek_dai.codec_of_node = codec_np;
/* Parse dai and platform info */
at91sam9g20ek_dai.cpu_dai_name = NULL;
at91sam9g20ek_dai.platform_name = NULL;
cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
if (!cpu_np) {
dev_err(&pdev->dev, "dai and pcm info missing\n");
return -EINVAL;
}
at91sam9g20ek_dai.cpu_of_node = cpu_np;
at91sam9g20ek_dai.platform_of_node = cpu_np;
of_node_put(codec_np);
of_node_put(cpu_np);
} }
platform_set_drvdata(at91sam9g20ek_snd_device, ret = snd_soc_register_card(card);
&snd_soc_at91sam9g20ek);
ret = platform_device_add(at91sam9g20ek_snd_device);
if (ret) { if (ret) {
printk(KERN_ERR "ASoC: Platform device allocation failed\n"); printk(KERN_ERR "ASoC: snd_soc_register_card() failed\n");
goto err_device_add;
} }
return ret; return ret;
err_device_add:
platform_device_put(at91sam9g20ek_snd_device);
err_mclk: err_mclk:
clk_put(mclk); clk_put(mclk);
mclk = NULL; mclk = NULL;
err: err:
atmel_ssc_put_audio(0);
return ret; return ret;
} }
static void __exit at91sam9g20ek_exit(void) static int __devexit at91sam9g20ek_audio_remove(struct platform_device *pdev)
{ {
platform_device_unregister(at91sam9g20ek_snd_device); struct snd_soc_card *card = platform_get_drvdata(pdev);
at91sam9g20ek_snd_device = NULL;
atmel_ssc_put_audio(0);
snd_soc_unregister_card(card);
clk_put(mclk); clk_put(mclk);
mclk = NULL; mclk = NULL;
return 0;
} }
module_init(at91sam9g20ek_init); #ifdef CONFIG_OF
module_exit(at91sam9g20ek_exit); static const struct of_device_id at91sam9g20ek_wm8731_dt_ids[] = {
{ .compatible = "atmel,at91sam9g20ek-wm8731-audio", },
{ }
};
MODULE_DEVICE_TABLE(of, at91sam9g20ek_wm8731_dt_ids);
#endif
static struct platform_driver at91sam9g20ek_audio_driver = {
.driver = {
.name = "at91sam9g20ek-audio",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(at91sam9g20ek_wm8731_dt_ids),
},
.probe = at91sam9g20ek_audio_probe,
.remove = __devexit_p(at91sam9g20ek_audio_remove),
};
module_platform_driver(at91sam9g20ek_audio_driver);
/* Module information */ /* Module information */
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>"); MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731"); MODULE_DESCRIPTION("ALSA SoC AT91SAM9G20EK_WM8731");
MODULE_ALIAS("platform:at91sam9g20ek-audio");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment