Merge remote-tracking branch 'asoc/topic/atmel' into asoc-next
This commit is contained in:
commit
3bc3249226
15
Documentation/devicetree/bindings/misc/atmel-ssc.txt
Normal file
15
Documentation/devicetree/bindings/misc/atmel-ssc.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
* 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>;
|
||||||
|
};
|
|
@ -0,0 +1,26 @@
|
||||||
|
* 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 @@
|
||||||
tcb0 = &tcb0;
|
tcb0 = &tcb0;
|
||||||
tcb1 = &tcb1;
|
tcb1 = &tcb1;
|
||||||
i2c0 = &i2c0;
|
i2c0 = &i2c0;
|
||||||
|
ssc0 = &ssc0;
|
||||||
};
|
};
|
||||||
cpus {
|
cpus {
|
||||||
cpu@0 {
|
cpu@0 {
|
||||||
|
@ -212,6 +213,13 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
|
|
||||||
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 @@
|
||||||
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 @@
|
||||||
reg = <0x50>;
|
reg = <0x50>;
|
||||||
};
|
};
|
||||||
|
|
||||||
wm8731@1b {
|
wm8731: wm8731@1b {
|
||||||
compatible = "wm8731";
|
compatible = "wm8731";
|
||||||
reg = <0x1b>;
|
reg = <0x1b>;
|
||||||
};
|
};
|
||||||
|
@ -139,4 +154,19 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
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 @@
|
||||||
i2c0 = &i2c0;
|
i2c0 = &i2c0;
|
||||||
i2c1 = &i2c1;
|
i2c1 = &i2c1;
|
||||||
i2c2 = &i2c2;
|
i2c2 = &i2c2;
|
||||||
|
ssc0 = &ssc0;
|
||||||
};
|
};
|
||||||
cpus {
|
cpus {
|
||||||
cpu@0 {
|
cpu@0 {
|
||||||
|
@ -87,6 +88,13 @@
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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 = {
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
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[] = {
|
||||||
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[] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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)
|
||||||
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 @@
|
||||||
#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)
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
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 @@
|
||||||
#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
|
||||||
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
|
||||||
|
|
||||||
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
|
||||||
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
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
# 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
|
||||||
|
|
240
sound/soc/atmel/atmel-pcm-dma.c
Normal file
240
sound/soc/atmel/atmel-pcm-dma.c
Normal file
|
@ -0,0 +1,240 @@
|
||||||
|
/*
|
||||||
|
* 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");
|
401
sound/soc/atmel/atmel-pcm-pdc.c
Normal file
401
sound/soc/atmel/atmel-pcm-pdc.c
Normal file
|
@ -0,0 +1,401 @@
|
||||||
|
/*
|
||||||
|
* atmel-pcm.c -- ALSA PCM interface for the Atmel atmel SoC.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2005 SAN People
|
||||||
|
* Copyright (C) 2008 Atmel
|
||||||
|
*
|
||||||
|
* Authors: Sedji Gaouaou <sedji.gaouaou@atmel.com>
|
||||||
|
*
|
||||||
|
* Based on at91-pcm. by:
|
||||||
|
* Frank Mandarino <fmandarino@endrelia.com>
|
||||||
|
* Copyright 2006 Endrelia Technologies Inc.
|
||||||
|
*
|
||||||
|
* Based on pxa2xx-pcm.c by:
|
||||||
|
*
|
||||||
|
* Author: Nicolas Pitre
|
||||||
|
* Created: Nov 30, 2004
|
||||||
|
* Copyright: (C) 2004 MontaVista Software, Inc.
|
||||||
|
*
|
||||||
|
* 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/atmel_pdc.h>
|
||||||
|
#include <linux/atmel-ssc.h>
|
||||||
|
|
||||||
|
#include <sound/core.h>
|
||||||
|
#include <sound/pcm.h>
|
||||||
|
#include <sound/pcm_params.h>
|
||||||
|
#include <sound/soc.h>
|
||||||
|
|
||||||
|
#include "atmel-pcm.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*\
|
||||||
|
* Hardware definition
|
||||||
|
\*--------------------------------------------------------------------------*/
|
||||||
|
/* TODO: These values were taken from the AT91 platform driver, check
|
||||||
|
* them against real values for AT32
|
||||||
|
*/
|
||||||
|
static const struct snd_pcm_hardware atmel_pcm_hardware = {
|
||||||
|
.info = SNDRV_PCM_INFO_MMAP |
|
||||||
|
SNDRV_PCM_INFO_MMAP_VALID |
|
||||||
|
SNDRV_PCM_INFO_INTERLEAVED |
|
||||||
|
SNDRV_PCM_INFO_PAUSE,
|
||||||
|
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
||||||
|
.period_bytes_min = 32,
|
||||||
|
.period_bytes_max = 8192,
|
||||||
|
.periods_min = 2,
|
||||||
|
.periods_max = 1024,
|
||||||
|
.buffer_bytes_max = ATMEL_SSC_DMABUF_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*\
|
||||||
|
* Data types
|
||||||
|
\*--------------------------------------------------------------------------*/
|
||||||
|
struct atmel_runtime_data {
|
||||||
|
struct atmel_pcm_dma_params *params;
|
||||||
|
dma_addr_t dma_buffer; /* physical address of dma buffer */
|
||||||
|
dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
|
||||||
|
size_t period_size;
|
||||||
|
|
||||||
|
dma_addr_t period_ptr; /* physical address of next period */
|
||||||
|
|
||||||
|
/* PDC register save */
|
||||||
|
u32 pdc_xpr_save;
|
||||||
|
u32 pdc_xcr_save;
|
||||||
|
u32 pdc_xnpr_save;
|
||||||
|
u32 pdc_xncr_save;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*\
|
||||||
|
* ISR
|
||||||
|
\*--------------------------------------------------------------------------*/
|
||||||
|
static void atmel_pcm_dma_irq(u32 ssc_sr,
|
||||||
|
struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||||
|
struct atmel_pcm_dma_params *params = prtd->params;
|
||||||
|
static int count;
|
||||||
|
|
||||||
|
count++;
|
||||||
|
|
||||||
|
if (ssc_sr & params->mask->ssc_endbuf) {
|
||||||
|
pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x, count=%d)\n",
|
||||||
|
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
||||||
|
? "underrun" : "overrun",
|
||||||
|
params->name, ssc_sr, count);
|
||||||
|
|
||||||
|
/* re-start the PDC */
|
||||||
|
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||||
|
params->mask->pdc_disable);
|
||||||
|
prtd->period_ptr += prtd->period_size;
|
||||||
|
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
||||||
|
prtd->period_ptr = prtd->dma_buffer;
|
||||||
|
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
||||||
|
prtd->period_ptr);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
||||||
|
prtd->period_size / params->pdc_xfer_size);
|
||||||
|
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||||
|
params->mask->pdc_enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ssc_sr & params->mask->ssc_endx) {
|
||||||
|
/* Load the PDC next pointer and counter registers */
|
||||||
|
prtd->period_ptr += prtd->period_size;
|
||||||
|
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
||||||
|
prtd->period_ptr = prtd->dma_buffer;
|
||||||
|
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
||||||
|
prtd->period_ptr);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
||||||
|
prtd->period_size / params->pdc_xfer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
snd_pcm_period_elapsed(substream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*\
|
||||||
|
* PCM operations
|
||||||
|
\*--------------------------------------------------------------------------*/
|
||||||
|
static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||||
|
struct snd_pcm_hw_params *params)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct atmel_runtime_data *prtd = runtime->private_data;
|
||||||
|
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||||
|
|
||||||
|
/* this may get called several times by oss emulation
|
||||||
|
* with different params */
|
||||||
|
|
||||||
|
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
||||||
|
runtime->dma_bytes = params_buffer_bytes(params);
|
||||||
|
|
||||||
|
prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
||||||
|
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
|
||||||
|
|
||||||
|
prtd->dma_buffer = runtime->dma_addr;
|
||||||
|
prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
|
||||||
|
prtd->period_size = params_period_bytes(params);
|
||||||
|
|
||||||
|
pr_debug("atmel-pcm: "
|
||||||
|
"hw_params: DMA for %s initialized "
|
||||||
|
"(dma_bytes=%u, period_size=%u)\n",
|
||||||
|
prtd->params->name,
|
||||||
|
runtime->dma_bytes,
|
||||||
|
prtd->period_size);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||||
|
struct atmel_pcm_dma_params *params = prtd->params;
|
||||||
|
|
||||||
|
if (params != NULL) {
|
||||||
|
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
||||||
|
params->mask->pdc_disable);
|
||||||
|
prtd->params->dma_intr_handler = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||||
|
struct atmel_pcm_dma_params *params = prtd->params;
|
||||||
|
|
||||||
|
ssc_writex(params->ssc->regs, SSC_IDR,
|
||||||
|
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||||
|
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||||
|
params->mask->pdc_disable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
|
||||||
|
int cmd)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *rtd = substream->runtime;
|
||||||
|
struct atmel_runtime_data *prtd = rtd->private_data;
|
||||||
|
struct atmel_pcm_dma_params *params = prtd->params;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pr_debug("atmel-pcm:buffer_size = %ld,"
|
||||||
|
"dma_area = %p, dma_bytes = %u\n",
|
||||||
|
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case SNDRV_PCM_TRIGGER_START:
|
||||||
|
prtd->period_ptr = prtd->dma_buffer;
|
||||||
|
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
||||||
|
prtd->period_ptr);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
||||||
|
prtd->period_size / params->pdc_xfer_size);
|
||||||
|
|
||||||
|
prtd->period_ptr += prtd->period_size;
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
||||||
|
prtd->period_ptr);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
||||||
|
prtd->period_size / params->pdc_xfer_size);
|
||||||
|
|
||||||
|
pr_debug("atmel-pcm: trigger: "
|
||||||
|
"period_ptr=%lx, xpr=%u, "
|
||||||
|
"xcr=%u, xnpr=%u, xncr=%u\n",
|
||||||
|
(unsigned long)prtd->period_ptr,
|
||||||
|
ssc_readx(params->ssc->regs, params->pdc->xpr),
|
||||||
|
ssc_readx(params->ssc->regs, params->pdc->xcr),
|
||||||
|
ssc_readx(params->ssc->regs, params->pdc->xnpr),
|
||||||
|
ssc_readx(params->ssc->regs, params->pdc->xncr));
|
||||||
|
|
||||||
|
ssc_writex(params->ssc->regs, SSC_IER,
|
||||||
|
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
||||||
|
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
||||||
|
params->mask->pdc_enable);
|
||||||
|
|
||||||
|
pr_debug("sr=%u imr=%u\n",
|
||||||
|
ssc_readx(params->ssc->regs, SSC_SR),
|
||||||
|
ssc_readx(params->ssc->regs, SSC_IER));
|
||||||
|
break; /* SNDRV_PCM_TRIGGER_START */
|
||||||
|
|
||||||
|
case SNDRV_PCM_TRIGGER_STOP:
|
||||||
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||||
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||||
|
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||||
|
params->mask->pdc_disable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SNDRV_PCM_TRIGGER_RESUME:
|
||||||
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||||
|
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
||||||
|
params->mask->pdc_enable);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static snd_pcm_uframes_t atmel_pcm_pointer(
|
||||||
|
struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct atmel_runtime_data *prtd = runtime->private_data;
|
||||||
|
struct atmel_pcm_dma_params *params = prtd->params;
|
||||||
|
dma_addr_t ptr;
|
||||||
|
snd_pcm_uframes_t x;
|
||||||
|
|
||||||
|
ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
|
||||||
|
x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
|
||||||
|
|
||||||
|
if (x == runtime->buffer_size)
|
||||||
|
x = 0;
|
||||||
|
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_pcm_open(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||||
|
struct atmel_runtime_data *prtd;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
|
||||||
|
|
||||||
|
/* ensure that buffer size is a multiple of period size */
|
||||||
|
ret = snd_pcm_hw_constraint_integer(runtime,
|
||||||
|
SNDRV_PCM_HW_PARAM_PERIODS);
|
||||||
|
if (ret < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
|
||||||
|
if (prtd == NULL) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
runtime->private_data = prtd;
|
||||||
|
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_pcm_close(struct snd_pcm_substream *substream)
|
||||||
|
{
|
||||||
|
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
||||||
|
|
||||||
|
kfree(prtd);
|
||||||
|
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,
|
||||||
|
.hw_free = atmel_pcm_hw_free,
|
||||||
|
.prepare = atmel_pcm_prepare,
|
||||||
|
.trigger = atmel_pcm_trigger,
|
||||||
|
.pointer = atmel_pcm_pointer,
|
||||||
|
.mmap = atmel_pcm_mmap,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------*\
|
||||||
|
* ASoC platform driver
|
||||||
|
\*--------------------------------------------------------------------------*/
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int atmel_pcm_suspend(struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||||
|
struct atmel_runtime_data *prtd;
|
||||||
|
struct atmel_pcm_dma_params *params;
|
||||||
|
|
||||||
|
if (!runtime)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
prtd = runtime->private_data;
|
||||||
|
params = prtd->params;
|
||||||
|
|
||||||
|
/* disable the PDC and save the PDC registers */
|
||||||
|
|
||||||
|
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
|
||||||
|
|
||||||
|
prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
|
||||||
|
prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
|
||||||
|
prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
|
||||||
|
prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int atmel_pcm_resume(struct snd_soc_dai *dai)
|
||||||
|
{
|
||||||
|
struct snd_pcm_runtime *runtime = dai->runtime;
|
||||||
|
struct atmel_runtime_data *prtd;
|
||||||
|
struct atmel_pcm_dma_params *params;
|
||||||
|
|
||||||
|
if (!runtime)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
prtd = runtime->private_data;
|
||||||
|
params = prtd->params;
|
||||||
|
|
||||||
|
/* restore the PDC registers and enable the PDC */
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
|
||||||
|
ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
|
||||||
|
|
||||||
|
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define atmel_pcm_suspend NULL
|
||||||
|
#define atmel_pcm_resume NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct snd_soc_platform_driver atmel_soc_platform = {
|
||||||
|
.ops = &atmel_pcm_ops,
|
||||||
|
.pcm_new = atmel_pcm_new,
|
||||||
|
.pcm_free = atmel_pcm_free,
|
||||||
|
.suspend = atmel_pcm_suspend,
|
||||||
|
.resume = atmel_pcm_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
int atmel_pcm_pdc_platform_register(struct device *dev)
|
||||||
|
{
|
||||||
|
return snd_soc_register_platform(dev, &atmel_soc_platform);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(atmel_pcm_pdc_platform_register);
|
||||||
|
|
||||||
|
void atmel_pcm_pdc_platform_unregister(struct device *dev)
|
||||||
|
{
|
||||||
|
snd_soc_unregister_platform(dev);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(atmel_pcm_pdc_platform_unregister);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
|
||||||
|
MODULE_DESCRIPTION("Atmel PCM module");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -32,80 +32,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/dma-mapping.h>
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/atmel_pdc.h>
|
|
||||||
#include <linux/atmel-ssc.h>
|
|
||||||
|
|
||||||
#include <sound/core.h>
|
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
#include <sound/pcm_params.h>
|
|
||||||
#include <sound/soc.h>
|
#include <sound/soc.h>
|
||||||
|
|
||||||
#include "atmel-pcm.h"
|
#include "atmel-pcm.h"
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*\
|
|
||||||
* Hardware definition
|
|
||||||
\*--------------------------------------------------------------------------*/
|
|
||||||
/* TODO: These values were taken from the AT91 platform driver, check
|
|
||||||
* them against real values for AT32
|
|
||||||
*/
|
|
||||||
static const struct snd_pcm_hardware atmel_pcm_hardware = {
|
|
||||||
.info = SNDRV_PCM_INFO_MMAP |
|
|
||||||
SNDRV_PCM_INFO_MMAP_VALID |
|
|
||||||
SNDRV_PCM_INFO_INTERLEAVED |
|
|
||||||
SNDRV_PCM_INFO_PAUSE,
|
|
||||||
.formats = SNDRV_PCM_FMTBIT_S16_LE,
|
|
||||||
.period_bytes_min = 32,
|
|
||||||
.period_bytes_max = 8192,
|
|
||||||
.periods_min = 2,
|
|
||||||
.periods_max = 1024,
|
|
||||||
.buffer_bytes_max = 32 * 1024,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*\
|
|
||||||
* Data types
|
|
||||||
\*--------------------------------------------------------------------------*/
|
|
||||||
struct atmel_runtime_data {
|
|
||||||
struct atmel_pcm_dma_params *params;
|
|
||||||
dma_addr_t dma_buffer; /* physical address of dma buffer */
|
|
||||||
dma_addr_t dma_buffer_end; /* first address beyond DMA buffer */
|
|
||||||
size_t period_size;
|
|
||||||
|
|
||||||
dma_addr_t period_ptr; /* physical address of next period */
|
|
||||||
|
|
||||||
/* PDC register save */
|
|
||||||
u32 pdc_xpr_save;
|
|
||||||
u32 pdc_xcr_save;
|
|
||||||
u32 pdc_xnpr_save;
|
|
||||||
u32 pdc_xncr_save;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*\
|
|
||||||
* Helper functions
|
|
||||||
\*--------------------------------------------------------------------------*/
|
|
||||||
static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||||
int stream)
|
int stream)
|
||||||
{
|
{
|
||||||
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
struct snd_pcm_substream *substream = pcm->streams[stream].substream;
|
||||||
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
struct snd_dma_buffer *buf = &substream->dma_buffer;
|
||||||
size_t size = atmel_pcm_hardware.buffer_bytes_max;
|
size_t size = ATMEL_SSC_DMABUF_SIZE;
|
||||||
|
|
||||||
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
buf->dev.type = SNDRV_DMA_TYPE_DEV;
|
||||||
buf->dev.dev = pcm->card->dev;
|
buf->dev.dev = pcm->card->dev;
|
||||||
buf->private_data = NULL;
|
buf->private_data = NULL;
|
||||||
buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
buf->area = dma_alloc_coherent(pcm->card->dev, size,
|
||||||
&buf->addr, GFP_KERNEL);
|
&buf->addr, GFP_KERNEL);
|
||||||
pr_debug("atmel-pcm:"
|
pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n",
|
||||||
"preallocate_dma_buffer: area=%p, addr=%p, size=%d\n",
|
(void *)buf->area, (void *)buf->addr, size);
|
||||||
(void *) buf->area,
|
|
||||||
(void *) buf->addr,
|
|
||||||
size);
|
|
||||||
|
|
||||||
if (!buf->area)
|
if (!buf->area)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -113,258 +58,19 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
|
||||||
buf->bytes = size;
|
buf->bytes = size;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
/*--------------------------------------------------------------------------*\
|
|
||||||
* ISR
|
|
||||||
\*--------------------------------------------------------------------------*/
|
|
||||||
static void atmel_pcm_dma_irq(u32 ssc_sr,
|
|
||||||
struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
|
||||||
struct atmel_pcm_dma_params *params = prtd->params;
|
|
||||||
static int count;
|
|
||||||
|
|
||||||
count++;
|
int atmel_pcm_mmap(struct snd_pcm_substream *substream,
|
||||||
|
|
||||||
if (ssc_sr & params->mask->ssc_endbuf) {
|
|
||||||
pr_warning("atmel-pcm: buffer %s on %s"
|
|
||||||
" (SSC_SR=%#x, count=%d)\n",
|
|
||||||
substream->stream == SNDRV_PCM_STREAM_PLAYBACK
|
|
||||||
? "underrun" : "overrun",
|
|
||||||
params->name, ssc_sr, count);
|
|
||||||
|
|
||||||
/* re-start the PDC */
|
|
||||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
|
||||||
params->mask->pdc_disable);
|
|
||||||
prtd->period_ptr += prtd->period_size;
|
|
||||||
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
|
||||||
prtd->period_ptr = prtd->dma_buffer;
|
|
||||||
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
|
||||||
prtd->period_ptr);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
|
||||||
prtd->period_size / params->pdc_xfer_size);
|
|
||||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
|
||||||
params->mask->pdc_enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ssc_sr & params->mask->ssc_endx) {
|
|
||||||
/* Load the PDC next pointer and counter registers */
|
|
||||||
prtd->period_ptr += prtd->period_size;
|
|
||||||
if (prtd->period_ptr >= prtd->dma_buffer_end)
|
|
||||||
prtd->period_ptr = prtd->dma_buffer;
|
|
||||||
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
|
||||||
prtd->period_ptr);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
|
||||||
prtd->period_size / params->pdc_xfer_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
snd_pcm_period_elapsed(substream);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*\
|
|
||||||
* PCM operations
|
|
||||||
\*--------------------------------------------------------------------------*/
|
|
||||||
static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
||||||
struct snd_pcm_hw_params *params)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
struct atmel_runtime_data *prtd = runtime->private_data;
|
|
||||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
|
||||||
|
|
||||||
/* this may get called several times by oss emulation
|
|
||||||
* with different params */
|
|
||||||
|
|
||||||
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
|
|
||||||
runtime->dma_bytes = params_buffer_bytes(params);
|
|
||||||
|
|
||||||
prtd->params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
|
|
||||||
prtd->params->dma_intr_handler = atmel_pcm_dma_irq;
|
|
||||||
|
|
||||||
prtd->dma_buffer = runtime->dma_addr;
|
|
||||||
prtd->dma_buffer_end = runtime->dma_addr + runtime->dma_bytes;
|
|
||||||
prtd->period_size = params_period_bytes(params);
|
|
||||||
|
|
||||||
pr_debug("atmel-pcm: "
|
|
||||||
"hw_params: DMA for %s initialized "
|
|
||||||
"(dma_bytes=%u, period_size=%u)\n",
|
|
||||||
prtd->params->name,
|
|
||||||
runtime->dma_bytes,
|
|
||||||
prtd->period_size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_hw_free(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
|
||||||
struct atmel_pcm_dma_params *params = prtd->params;
|
|
||||||
|
|
||||||
if (params != NULL) {
|
|
||||||
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
|
||||||
params->mask->pdc_disable);
|
|
||||||
prtd->params->dma_intr_handler = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_prepare(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
|
||||||
struct atmel_pcm_dma_params *params = prtd->params;
|
|
||||||
|
|
||||||
ssc_writex(params->ssc->regs, SSC_IDR,
|
|
||||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
|
||||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
|
||||||
params->mask->pdc_disable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
|
|
||||||
int cmd)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *rtd = substream->runtime;
|
|
||||||
struct atmel_runtime_data *prtd = rtd->private_data;
|
|
||||||
struct atmel_pcm_dma_params *params = prtd->params;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
pr_debug("atmel-pcm:buffer_size = %ld,"
|
|
||||||
"dma_area = %p, dma_bytes = %u\n",
|
|
||||||
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case SNDRV_PCM_TRIGGER_START:
|
|
||||||
prtd->period_ptr = prtd->dma_buffer;
|
|
||||||
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xpr,
|
|
||||||
prtd->period_ptr);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xcr,
|
|
||||||
prtd->period_size / params->pdc_xfer_size);
|
|
||||||
|
|
||||||
prtd->period_ptr += prtd->period_size;
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xnpr,
|
|
||||||
prtd->period_ptr);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xncr,
|
|
||||||
prtd->period_size / params->pdc_xfer_size);
|
|
||||||
|
|
||||||
pr_debug("atmel-pcm: trigger: "
|
|
||||||
"period_ptr=%lx, xpr=%u, "
|
|
||||||
"xcr=%u, xnpr=%u, xncr=%u\n",
|
|
||||||
(unsigned long)prtd->period_ptr,
|
|
||||||
ssc_readx(params->ssc->regs, params->pdc->xpr),
|
|
||||||
ssc_readx(params->ssc->regs, params->pdc->xcr),
|
|
||||||
ssc_readx(params->ssc->regs, params->pdc->xnpr),
|
|
||||||
ssc_readx(params->ssc->regs, params->pdc->xncr));
|
|
||||||
|
|
||||||
ssc_writex(params->ssc->regs, SSC_IER,
|
|
||||||
params->mask->ssc_endx | params->mask->ssc_endbuf);
|
|
||||||
ssc_writex(params->ssc->regs, SSC_PDC_PTCR,
|
|
||||||
params->mask->pdc_enable);
|
|
||||||
|
|
||||||
pr_debug("sr=%u imr=%u\n",
|
|
||||||
ssc_readx(params->ssc->regs, SSC_SR),
|
|
||||||
ssc_readx(params->ssc->regs, SSC_IER));
|
|
||||||
break; /* SNDRV_PCM_TRIGGER_START */
|
|
||||||
|
|
||||||
case SNDRV_PCM_TRIGGER_STOP:
|
|
||||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
||||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
|
||||||
params->mask->pdc_disable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case SNDRV_PCM_TRIGGER_RESUME:
|
|
||||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
||||||
ssc_writex(params->ssc->regs, ATMEL_PDC_PTCR,
|
|
||||||
params->mask->pdc_enable);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
ret = -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static snd_pcm_uframes_t atmel_pcm_pointer(
|
|
||||||
struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
struct atmel_runtime_data *prtd = runtime->private_data;
|
|
||||||
struct atmel_pcm_dma_params *params = prtd->params;
|
|
||||||
dma_addr_t ptr;
|
|
||||||
snd_pcm_uframes_t x;
|
|
||||||
|
|
||||||
ptr = (dma_addr_t) ssc_readx(params->ssc->regs, params->pdc->xpr);
|
|
||||||
x = bytes_to_frames(runtime, ptr - prtd->dma_buffer);
|
|
||||||
|
|
||||||
if (x == runtime->buffer_size)
|
|
||||||
x = 0;
|
|
||||||
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_open(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
||||||
struct atmel_runtime_data *prtd;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
snd_soc_set_runtime_hwparams(substream, &atmel_pcm_hardware);
|
|
||||||
|
|
||||||
/* ensure that buffer size is a multiple of period size */
|
|
||||||
ret = snd_pcm_hw_constraint_integer(runtime,
|
|
||||||
SNDRV_PCM_HW_PARAM_PERIODS);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
prtd = kzalloc(sizeof(struct atmel_runtime_data), GFP_KERNEL);
|
|
||||||
if (prtd == NULL) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
runtime->private_data = prtd;
|
|
||||||
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_close(struct snd_pcm_substream *substream)
|
|
||||||
{
|
|
||||||
struct atmel_runtime_data *prtd = substream->runtime->private_data;
|
|
||||||
|
|
||||||
kfree(prtd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
|
|
||||||
struct vm_area_struct *vma)
|
struct vm_area_struct *vma)
|
||||||
{
|
{
|
||||||
return remap_pfn_range(vma, vma->vm_start,
|
return remap_pfn_range(vma, vma->vm_start,
|
||||||
substream->dma_buffer.addr >> PAGE_SHIFT,
|
substream->dma_buffer.addr >> PAGE_SHIFT,
|
||||||
vma->vm_end - vma->vm_start, vma->vm_page_prot);
|
vma->vm_end - vma->vm_start, vma->vm_page_prot);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(atmel_pcm_mmap);
|
||||||
|
|
||||||
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,
|
|
||||||
.hw_free = atmel_pcm_hw_free,
|
|
||||||
.prepare = atmel_pcm_prepare,
|
|
||||||
.trigger = atmel_pcm_trigger,
|
|
||||||
.pointer = atmel_pcm_pointer,
|
|
||||||
.mmap = atmel_pcm_mmap,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/*--------------------------------------------------------------------------*\
|
|
||||||
* ASoC platform driver
|
|
||||||
\*--------------------------------------------------------------------------*/
|
|
||||||
static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32);
|
static u64 atmel_pcm_dmamask = DMA_BIT_MASK(32);
|
||||||
|
|
||||||
static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
{
|
{
|
||||||
struct snd_card *card = rtd->card->snd_card;
|
struct snd_card *card = rtd->card->snd_card;
|
||||||
struct snd_pcm *pcm = rtd->pcm;
|
struct snd_pcm *pcm = rtd->pcm;
|
||||||
|
@ -376,6 +82,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
|
||||||
|
|
||||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
|
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
|
||||||
|
pr_debug("atmel-pcm: allocating PCM playback DMA buffer\n");
|
||||||
ret = atmel_pcm_preallocate_dma_buffer(pcm,
|
ret = atmel_pcm_preallocate_dma_buffer(pcm,
|
||||||
SNDRV_PCM_STREAM_PLAYBACK);
|
SNDRV_PCM_STREAM_PLAYBACK);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -383,8 +90,7 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
|
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
|
||||||
pr_debug("atmel-pcm:"
|
pr_debug("atmel-pcm: allocating PCM capture DMA buffer\n");
|
||||||
"Allocating PCM capture DMA buffer\n");
|
|
||||||
ret = atmel_pcm_preallocate_dma_buffer(pcm,
|
ret = atmel_pcm_preallocate_dma_buffer(pcm,
|
||||||
SNDRV_PCM_STREAM_CAPTURE);
|
SNDRV_PCM_STREAM_CAPTURE);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -393,8 +99,9 @@ static int atmel_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(atmel_pcm_new);
|
||||||
|
|
||||||
static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
void atmel_pcm_free(struct snd_pcm *pcm)
|
||||||
{
|
{
|
||||||
struct snd_pcm_substream *substream;
|
struct snd_pcm_substream *substream;
|
||||||
struct snd_dma_buffer *buf;
|
struct snd_dma_buffer *buf;
|
||||||
|
@ -413,89 +120,5 @@ static void atmel_pcm_free_dma_buffers(struct snd_pcm *pcm)
|
||||||
buf->area = NULL;
|
buf->area = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(atmel_pcm_free);
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
|
||||||
static int atmel_pcm_suspend(struct snd_soc_dai *dai)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
|
||||||
struct atmel_runtime_data *prtd;
|
|
||||||
struct atmel_pcm_dma_params *params;
|
|
||||||
|
|
||||||
if (!runtime)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
prtd = runtime->private_data;
|
|
||||||
params = prtd->params;
|
|
||||||
|
|
||||||
/* disable the PDC and save the PDC registers */
|
|
||||||
|
|
||||||
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_disable);
|
|
||||||
|
|
||||||
prtd->pdc_xpr_save = ssc_readx(params->ssc->regs, params->pdc->xpr);
|
|
||||||
prtd->pdc_xcr_save = ssc_readx(params->ssc->regs, params->pdc->xcr);
|
|
||||||
prtd->pdc_xnpr_save = ssc_readx(params->ssc->regs, params->pdc->xnpr);
|
|
||||||
prtd->pdc_xncr_save = ssc_readx(params->ssc->regs, params->pdc->xncr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int atmel_pcm_resume(struct snd_soc_dai *dai)
|
|
||||||
{
|
|
||||||
struct snd_pcm_runtime *runtime = dai->runtime;
|
|
||||||
struct atmel_runtime_data *prtd;
|
|
||||||
struct atmel_pcm_dma_params *params;
|
|
||||||
|
|
||||||
if (!runtime)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
prtd = runtime->private_data;
|
|
||||||
params = prtd->params;
|
|
||||||
|
|
||||||
/* restore the PDC registers and enable the PDC */
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xpr, prtd->pdc_xpr_save);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xcr, prtd->pdc_xcr_save);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xnpr, prtd->pdc_xnpr_save);
|
|
||||||
ssc_writex(params->ssc->regs, params->pdc->xncr, prtd->pdc_xncr_save);
|
|
||||||
|
|
||||||
ssc_writel(params->ssc->regs, PDC_PTCR, params->mask->pdc_enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define atmel_pcm_suspend NULL
|
|
||||||
#define atmel_pcm_resume NULL
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct snd_soc_platform_driver atmel_soc_platform = {
|
|
||||||
.ops = &atmel_pcm_ops,
|
|
||||||
.pcm_new = atmel_pcm_new,
|
|
||||||
.pcm_free = atmel_pcm_free_dma_buffers,
|
|
||||||
.suspend = atmel_pcm_suspend,
|
|
||||||
.resume = atmel_pcm_resume,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int __devinit atmel_soc_platform_probe(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
return snd_soc_register_platform(&pdev->dev, &atmel_soc_platform);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __devexit atmel_soc_platform_remove(struct platform_device *pdev)
|
|
||||||
{
|
|
||||||
snd_soc_unregister_platform(&pdev->dev);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct platform_driver atmel_pcm_driver = {
|
|
||||||
.driver = {
|
|
||||||
.name = "atmel-pcm-audio",
|
|
||||||
.owner = THIS_MODULE,
|
|
||||||
},
|
|
||||||
|
|
||||||
.probe = atmel_soc_platform_probe,
|
|
||||||
.remove = __devexit_p(atmel_soc_platform_remove),
|
|
||||||
};
|
|
||||||
|
|
||||||
module_platform_driver(atmel_pcm_driver);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Sedji Gaouaou <sedji.gaouaou@atmel.com>");
|
|
||||||
MODULE_DESCRIPTION("Atmel PCM module");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
|
@ -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 {
|
||||||
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 {
|
||||||
#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 @@
|
||||||
#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] = {
|
||||||
.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] = {
|
||||||
.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] = {
|
||||||
.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] = {
|
||||||
.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)
|
||||||
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,11 +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,
|
.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 = {
|
||||||
|
@ -746,69 +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,
|
||||||
},
|
|
||||||
#if NUM_SSC_DEVICES == 3
|
|
||||||
{
|
|
||||||
.name = "atmel-ssc-dai.1",
|
|
||||||
.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,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
.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 = {
|
||||||
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 {
|
||||||
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 @@
|
||||||
#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)
|
||||||
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 = {
|
||||||
.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);
|
||||||
|
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;
|
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)
|
||||||
|
|
||||||
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");
|
||||||
|
|
Loading…
Reference in a new issue