Commit 2944c2f5 authored by Mark Brown's avatar Mark Brown

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

parents f8843c91 5b1d3c34
Samsung SMDK audio complex
Required properties:
- compatible : "samsung,smdk-wm8994"
- samsung,i2s-controller: The phandle of the Samsung I2S0 controller
- samsung,audio-codec: The phandle of the WM8994 audio codec
Example:
sound {
compatible = "samsung,smdk-wm8994";
samsung,i2s-controller = <&i2s0>;
samsung,audio-codec = <&wm8994>;
};
* Samsung I2S controller
Required SoC Specific Properties:
- compatible : "samsung,i2s-v5"
- reg: physical base address of the controller and length of memory mapped
region.
- dmas: list of DMA controller phandle and DMA request line ordered pairs.
- dma-names: identifier string for each DMA request line in the dmas property.
These strings correspond 1:1 with the ordered pairs in dmas.
Optional SoC Specific Properties:
- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel
support, this flag is enabled.
- samsung,supports-rstclr: This flag should be set if I2S software reset bit
control is required. When this flag is set I2S software reset bit will be
enabled or disabled based on need.
- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA,
then this flag is enabled.
- samsung,idma-addr: Internal DMA register base address of the audio
sub system(used in secondary sound source).
Required Board Specific Properties:
- gpios: The gpio specifier for data out,data in, LRCLK, CDCLK and SCLK
interface lines. The format of the gpio specifier depends on the gpio
controller.
The syntax of samsung gpio specifier is
<[phandle of the gpio controller node]
[pin number within the gpio controller]
[mux function]
[flags and pull up/down]
[drive strength]>
Example:
- SoC Specific Portion:
i2s@03830000 {
compatible = "samsung,i2s-v5";
reg = <0x03830000 0x100>;
dmas = <&pdma0 10
&pdma0 9
&pdma0 8>;
dma-names = "tx", "rx", "tx-sec";
samsung,supports-6ch;
samsung,supports-rstclr;
samsung,supports-secdai;
samsung,idma-addr = <0x03000000>;
};
- Board Specific Portion:
i2s@03830000 {
gpios = <&gpz 0 2 0 0>, /* I2S_0_SCLK */
<&gpz 1 2 0 0>, /* I2S_0_CDCLK */
<&gpz 2 2 0 0>, /* I2S_0_LRCK */
<&gpz 3 2 0 0>, /* I2S_0_SDI */
<&gpz 4 2 0 0>, /* I2S_0_SDO[1] */
<&gpz 5 2 0 0>, /* I2S_0_SDO[2] */
<&gpz 6 2 0 0>; /* I2S_0_SDO[3] */
};
...@@ -49,6 +49,11 @@ eeprom@51 { ...@@ -49,6 +49,11 @@ eeprom@51 {
compatible = "samsung,s524ad0xd1"; compatible = "samsung,s524ad0xd1";
reg = <0x51>; reg = <0x51>;
}; };
wm8994: wm8994@1a {
compatible = "wlf,wm8994";
reg = <0x1a>;
};
}; };
i2c@121D0000 { i2c@121D0000 {
...@@ -204,4 +209,25 @@ codec@11000000 { ...@@ -204,4 +209,25 @@ codec@11000000 {
samsung,mfc-r = <0x43000000 0x800000>; samsung,mfc-r = <0x43000000 0x800000>;
samsung,mfc-l = <0x51000000 0x800000>; samsung,mfc-l = <0x51000000 0x800000>;
}; };
i2s0: i2s@03830000 {
gpios = <&gpz 0 2 0 0>, <&gpz 1 2 0 0>, <&gpz 2 2 0 0>,
<&gpz 3 2 0 0>, <&gpz 4 2 0 0>, <&gpz 5 2 0 0>,
<&gpz 6 2 0 0>;
};
i2s1: i2s@12D60000 {
status = "disabled";
};
i2s2: i2s@12D70000 {
status = "disabled";
};
sound {
compatible = "samsung,smdk-wm8994";
samsung,i2s-controller = <&i2s0>;
samsung,audio-codec = <&wm8994>;
};
}; };
...@@ -211,8 +211,9 @@ spi_0: spi@12d20000 { ...@@ -211,8 +211,9 @@ spi_0: spi@12d20000 {
compatible = "samsung,exynos4210-spi"; compatible = "samsung,exynos4210-spi";
reg = <0x12d20000 0x100>; reg = <0x12d20000 0x100>;
interrupts = <0 66 0>; interrupts = <0 66 0>;
tx-dma-channel = <&pdma0 5>; /* preliminary */ dmas = <&pdma0 5
rx-dma-channel = <&pdma0 4>; /* preliminary */ &pdma0 4>;
dma-names = "tx", "rx";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
}; };
...@@ -221,8 +222,9 @@ spi_1: spi@12d30000 { ...@@ -221,8 +222,9 @@ spi_1: spi@12d30000 {
compatible = "samsung,exynos4210-spi"; compatible = "samsung,exynos4210-spi";
reg = <0x12d30000 0x100>; reg = <0x12d30000 0x100>;
interrupts = <0 67 0>; interrupts = <0 67 0>;
tx-dma-channel = <&pdma1 5>; /* preliminary */ dmas = <&pdma1 5
rx-dma-channel = <&pdma1 4>; /* preliminary */ &pdma1 4>;
dma-names = "tx", "rx";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
}; };
...@@ -231,8 +233,9 @@ spi_2: spi@12d40000 { ...@@ -231,8 +233,9 @@ spi_2: spi@12d40000 {
compatible = "samsung,exynos4210-spi"; compatible = "samsung,exynos4210-spi";
reg = <0x12d40000 0x100>; reg = <0x12d40000 0x100>;
interrupts = <0 68 0>; interrupts = <0 68 0>;
tx-dma-channel = <&pdma0 7>; /* preliminary */ dmas = <&pdma0 7
rx-dma-channel = <&pdma0 6>; /* preliminary */ &pdma0 6>;
dma-names = "tx", "rx";
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
}; };
...@@ -269,6 +272,35 @@ dwmmc_3: dwmmc3@12230000 { ...@@ -269,6 +272,35 @@ dwmmc_3: dwmmc3@12230000 {
#size-cells = <0>; #size-cells = <0>;
}; };
i2s0: i2s@03830000 {
compatible = "samsung,i2s-v5";
reg = <0x03830000 0x100>;
dmas = <&pdma0 10
&pdma0 9
&pdma0 8>;
dma-names = "tx", "rx", "tx-sec";
samsung,supports-6ch;
samsung,supports-rstclr;
samsung,supports-secdai;
samsung,idma-addr = <0x03000000>;
};
i2s1: i2s@12D60000 {
compatible = "samsung,i2s-v5";
reg = <0x12D60000 0x100>;
dmas = <&pdma1 12
&pdma1 11>;
dma-names = "tx", "rx";
};
i2s2: i2s@12D70000 {
compatible = "samsung,i2s-v5";
reg = <0x12D70000 0x100>;
dmas = <&pdma0 12
&pdma0 11>;
dma-names = "tx", "rx";
};
amba { amba {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <1>; #size-cells = <1>;
......
...@@ -104,6 +104,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { ...@@ -104,6 +104,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL), OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL),
OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000, OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000,
"exynos-tmu", NULL), "exynos-tmu", NULL),
OF_DEV_AUXDATA("samsung,i2s-v5", 0x03830000,
"samsung-i2s.0", NULL),
OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D60000,
"samsung-i2s.1", NULL),
OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D70000,
"samsung-i2s.2", NULL),
{}, {},
}; };
......
...@@ -19,7 +19,8 @@ ...@@ -19,7 +19,8 @@
#include <mach/dma.h> #include <mach/dma.h>
static unsigned samsung_dmadev_request(enum dma_ch dma_ch, static unsigned samsung_dmadev_request(enum dma_ch dma_ch,
struct samsung_dma_req *param) struct samsung_dma_req *param,
struct device *dev, char *ch_name)
{ {
dma_cap_mask_t mask; dma_cap_mask_t mask;
void *filter_param; void *filter_param;
...@@ -33,7 +34,12 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch, ...@@ -33,7 +34,12 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch,
*/ */
filter_param = (dma_ch == DMACH_DT_PROP) ? filter_param = (dma_ch == DMACH_DT_PROP) ?
(void *)param->dt_dmach_prop : (void *)dma_ch; (void *)param->dt_dmach_prop : (void *)dma_ch;
return (unsigned)dma_request_channel(mask, pl330_filter, filter_param);
if (dev->of_node)
return (unsigned)dma_request_slave_channel(dev, ch_name);
else
return (unsigned)dma_request_channel(mask, pl330_filter,
filter_param);
} }
static int samsung_dmadev_release(unsigned ch, void *param) static int samsung_dmadev_release(unsigned ch, void *param)
......
...@@ -39,7 +39,8 @@ struct samsung_dma_config { ...@@ -39,7 +39,8 @@ struct samsung_dma_config {
}; };
struct samsung_dma_ops { struct samsung_dma_ops {
unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param); unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param,
struct device *dev, char *ch_name);
int (*release)(unsigned ch, void *param); int (*release)(unsigned ch, void *param);
int (*config)(unsigned ch, struct samsung_dma_config *param); int (*config)(unsigned ch, struct samsung_dma_config *param);
int (*prepare)(unsigned ch, struct samsung_dma_prep *param); int (*prepare)(unsigned ch, struct samsung_dma_prep *param);
......
...@@ -36,7 +36,8 @@ static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param, ...@@ -36,7 +36,8 @@ static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param,
} }
static unsigned s3c_dma_request(enum dma_ch dma_ch, static unsigned s3c_dma_request(enum dma_ch dma_ch,
struct samsung_dma_req *param) struct samsung_dma_req *param,
struct device *dev, char *ch_name)
{ {
struct cb_data *data; struct cb_data *data;
......
...@@ -134,7 +134,6 @@ struct s3c64xx_spi_dma_data { ...@@ -134,7 +134,6 @@ struct s3c64xx_spi_dma_data {
unsigned ch; unsigned ch;
enum dma_transfer_direction direction; enum dma_transfer_direction direction;
enum dma_ch dmach; enum dma_ch dmach;
struct property *dma_prop;
}; };
/** /**
...@@ -319,16 +318,15 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, ...@@ -319,16 +318,15 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma,
static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) static int acquire_dma(struct s3c64xx_spi_driver_data *sdd)
{ {
struct samsung_dma_req req; struct samsung_dma_req req;
struct device *dev = &sdd->pdev->dev;
sdd->ops = samsung_dma_get_ops(); sdd->ops = samsung_dma_get_ops();
req.cap = DMA_SLAVE; req.cap = DMA_SLAVE;
req.client = &s3c64xx_spi_dma_client; req.client = &s3c64xx_spi_dma_client;
req.dt_dmach_prop = sdd->rx_dma.dma_prop; sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx");
sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req); sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx");
req.dt_dmach_prop = sdd->tx_dma.dma_prop;
sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req);
return 1; return 1;
} }
...@@ -1054,49 +1052,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) ...@@ -1054,49 +1052,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
flush_fifo(sdd); flush_fifo(sdd);
} }
static int s3c64xx_spi_get_dmares(
struct s3c64xx_spi_driver_data *sdd, bool tx)
{
struct platform_device *pdev = sdd->pdev;
struct s3c64xx_spi_dma_data *dma_data;
struct property *prop;
struct resource *res;
char prop_name[15], *chan_str;
if (tx) {
dma_data = &sdd->tx_dma;
dma_data->direction = DMA_MEM_TO_DEV;
chan_str = "tx";
} else {
dma_data = &sdd->rx_dma;
dma_data->direction = DMA_DEV_TO_MEM;
chan_str = "rx";
}
if (!sdd->pdev->dev.of_node) {
res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1);
if (!res) {
dev_err(&pdev->dev, "Unable to get SPI-%s dma "
"resource\n", chan_str);
return -ENXIO;
}
dma_data->dmach = res->start;
return 0;
}
sprintf(prop_name, "%s-dma-channel", chan_str);
prop = of_find_property(pdev->dev.of_node, prop_name, NULL);
if (!prop) {
dev_err(&pdev->dev, "%s dma channel property not specified\n",
chan_str);
return -ENXIO;
}
dma_data->dmach = DMACH_DT_PROP;
dma_data->dma_prop = prop;
return 0;
}
#ifdef CONFIG_OF #ifdef CONFIG_OF
static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd)
{ {
...@@ -1198,6 +1153,7 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( ...@@ -1198,6 +1153,7 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config(
static int __init s3c64xx_spi_probe(struct platform_device *pdev) static int __init s3c64xx_spi_probe(struct platform_device *pdev)
{ {
struct resource *mem_res; struct resource *mem_res;
struct resource *res;
struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_driver_data *sdd;
struct s3c64xx_spi_info *sci = pdev->dev.platform_data; struct s3c64xx_spi_info *sci = pdev->dev.platform_data;
struct spi_master *master; struct spi_master *master;
...@@ -1256,13 +1212,26 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) ...@@ -1256,13 +1212,26 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev)
sdd->cur_bpw = 8; sdd->cur_bpw = 8;
ret = s3c64xx_spi_get_dmares(sdd, true); if (!sdd->pdev->dev.of_node) {
if (ret) res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
goto err0; if (!res) {
dev_err(&pdev->dev, "Unable to get SPI tx dma "
"resource\n");
return -ENXIO;
}
sdd->tx_dma.dmach = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) {
dev_err(&pdev->dev, "Unable to get SPI rx dma "
"resource\n");
return -ENXIO;
}
sdd->rx_dma.dmach = res->start;
}
ret = s3c64xx_spi_get_dmares(sdd, false); sdd->tx_dma.direction = DMA_MEM_TO_DEV;
if (ret) sdd->rx_dma.direction = DMA_DEV_TO_MEM;
goto err0;
master->dev.of_node = pdev->dev.of_node; master->dev.of_node = pdev->dev.of_node;
master->bus_num = sdd->port_id; master->bus_num = sdd->port_id;
......
...@@ -63,7 +63,7 @@ config SND_SOC_SAMSUNG_SMDK_WM8580 ...@@ -63,7 +63,7 @@ config SND_SOC_SAMSUNG_SMDK_WM8580
config SND_SOC_SAMSUNG_SMDK_WM8994 config SND_SOC_SAMSUNG_SMDK_WM8994
tristate "SoC I2S Audio support for WM8994 on SMDK" tristate "SoC I2S Audio support for WM8994 on SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDKV310 || MACH_SMDKC210 || MACH_SMDK4212) depends on SND_SOC_SAMSUNG
depends on I2C=y && GENERIC_HARDIRQS depends on I2C=y && GENERIC_HARDIRQS
select MFD_WM8994 select MFD_WM8994
select SND_SOC_WM8994 select SND_SOC_WM8994
...@@ -162,7 +162,7 @@ config SND_SOC_GONI_AQUILA_WM8994 ...@@ -162,7 +162,7 @@ config SND_SOC_GONI_AQUILA_WM8994
config SND_SOC_SAMSUNG_SMDK_SPDIF config SND_SOC_SAMSUNG_SMDK_SPDIF
tristate "SoC S/PDIF Audio support for SMDK" tristate "SoC S/PDIF Audio support for SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310 || MACH_SMDK4212) depends on SND_SOC_SAMSUNG
select SND_SAMSUNG_SPDIF select SND_SAMSUNG_SPDIF
help help
Say Y if you want to add support for SoC S/PDIF audio on the SMDK. Say Y if you want to add support for SoC S/PDIF audio on the SMDK.
...@@ -177,7 +177,7 @@ config SND_SOC_SMDK_WM8580_PCM ...@@ -177,7 +177,7 @@ config SND_SOC_SMDK_WM8580_PCM
config SND_SOC_SMDK_WM8994_PCM config SND_SOC_SMDK_WM8994_PCM
tristate "SoC PCM Audio support for WM8994 on SMDK" tristate "SoC PCM Audio support for WM8994 on SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310 || MACH_SMDK4212) depends on SND_SOC_SAMSUNG
depends on I2C=y && GENERIC_HARDIRQS depends on I2C=y && GENERIC_HARDIRQS
select MFD_WM8994 select MFD_WM8994
select SND_SOC_WM8994 select SND_SOC_WM8994
......
...@@ -174,7 +174,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream, ...@@ -174,7 +174,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
config.width = prtd->params->dma_size; config.width = prtd->params->dma_size;
config.fifo = prtd->params->dma_addr; config.fifo = prtd->params->dma_addr;
prtd->params->ch = prtd->params->ops->request( prtd->params->ch = prtd->params->ops->request(
prtd->params->channel, &req); prtd->params->channel, &req, rtd->cpu_dai->dev,
prtd->params->ch_name);
prtd->params->ops->config(prtd->params->ch, &config); prtd->params->ops->config(prtd->params->ch, &config);
} }
......
...@@ -19,6 +19,7 @@ struct s3c_dma_params { ...@@ -19,6 +19,7 @@ struct s3c_dma_params {
int dma_size; /* Size of the DMA transfer */ int dma_size; /* Size of the DMA transfer */
unsigned ch; unsigned ch;
struct samsung_dma_ops *ops; struct samsung_dma_ops *ops;
char *ch_name;
}; };
int asoc_dma_platform_register(struct device *dev); int asoc_dma_platform_register(struct device *dev);
......
...@@ -15,11 +15,15 @@ ...@@ -15,11 +15,15 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <mach/dma.h>
#include <linux/platform_data/asoc-s3c.h> #include <linux/platform_data/asoc-s3c.h>
#include "dma.h" #include "dma.h"
...@@ -29,6 +33,15 @@ ...@@ -29,6 +33,15 @@
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
enum samsung_dai_type {
TYPE_PRI,
TYPE_SEC,
};
struct samsung_i2s_dai_data {
int dai_type;
};
struct i2s_dai { struct i2s_dai {
/* Platform device for this DAI */ /* Platform device for this DAI */
struct platform_device *pdev; struct platform_device *pdev;
...@@ -66,6 +79,7 @@ struct i2s_dai { ...@@ -66,6 +79,7 @@ struct i2s_dai {
u32 suspend_i2smod; u32 suspend_i2smod;
u32 suspend_i2scon; u32 suspend_i2scon;
u32 suspend_i2spsr; u32 suspend_i2spsr;
unsigned long gpios[7]; /* i2s gpio line numbers */
}; };
/* Lock for cross i/f checks */ /* Lock for cross i/f checks */
...@@ -651,6 +665,9 @@ static int i2s_startup(struct snd_pcm_substream *substream, ...@@ -651,6 +665,9 @@ static int i2s_startup(struct snd_pcm_substream *substream,
/* Enforce set_sysclk in Master mode */ /* Enforce set_sysclk in Master mode */
i2s->rclk_srcrate = 0; i2s->rclk_srcrate = 0;
if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR))
writel(CON_RSTCLR, i2s->addr + I2SCON);
spin_unlock_irqrestore(&lock, flags); spin_unlock_irqrestore(&lock, flags);
return 0; return 0;
...@@ -981,8 +998,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) ...@@ -981,8 +998,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS;
} else { /* Create a new platform_device for Secondary */ } else { /* Create a new platform_device for Secondary */
i2s->pdev = platform_device_register_resndata(NULL, i2s->pdev = platform_device_register_resndata(NULL,
pdev->name, pdev->id + SAMSUNG_I2S_SECOFF, "samsung-i2s-sec", -1, NULL, 0, NULL, 0);
NULL, 0, NULL, 0);
if (IS_ERR(i2s->pdev)) if (IS_ERR(i2s->pdev))
return NULL; return NULL;
} }
...@@ -993,18 +1009,103 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) ...@@ -993,18 +1009,103 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
return i2s; return i2s;
} }
#ifdef CONFIG_OF
static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s)
{
struct device *dev = &i2s->pdev->dev;
int index, gpio, ret;
for (index = 0; index < 7; index++) {
gpio = of_get_gpio(dev->of_node, index);
if (!gpio_is_valid(gpio)) {
dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio);
goto free_gpio;
}
ret = gpio_request(gpio, dev_name(dev));
if (ret) {
dev_err(dev, "gpio [%d] request failed\n", gpio);
goto free_gpio;
}
i2s->gpios[index] = gpio;
}
return 0;
free_gpio:
while (--index >= 0)
gpio_free(i2s->gpios[index]);
return -EINVAL;
}
static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s)
{
unsigned int index;
for (index = 0; index < 7; index++)
gpio_free(i2s->gpios[index]);
}
#else
static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai)
{
return -EINVAL;
}
static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai)
{
}
#endif
static const struct of_device_id exynos_i2s_match[];
static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
{
#ifdef CONFIG_OF
struct samsung_i2s_dai_data *data;
if (pdev->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
data = (struct samsung_i2s_dai_data *) match->data;
return data->dai_type;
} else
#endif
return platform_get_device_id(pdev)->driver_data;
}
#ifdef CONFIG_PM_RUNTIME
static int i2s_runtime_suspend(struct device *dev)
{
struct i2s_dai *i2s = dev_get_drvdata(dev);
clk_disable_unprepare(i2s->clk);
return 0;
}
static int i2s_runtime_resume(struct device *dev)
{
struct i2s_dai *i2s = dev_get_drvdata(dev);
clk_prepare_enable(i2s->clk);
return 0;
}
#endif /* CONFIG_PM_RUNTIME */
static int samsung_i2s_probe(struct platform_device *pdev) static int samsung_i2s_probe(struct platform_device *pdev)
{ {
u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan;
struct i2s_dai *pri_dai, *sec_dai = NULL; struct i2s_dai *pri_dai, *sec_dai = NULL;
struct s3c_audio_pdata *i2s_pdata; struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
struct samsung_i2s *i2s_cfg; struct samsung_i2s *i2s_cfg = NULL;
struct resource *res; struct resource *res;
u32 regs_base, quirks; u32 regs_base, quirks = 0, idma_addr = 0;
struct device_node *np = pdev->dev.of_node;
enum samsung_dai_type samsung_dai_type;
int ret = 0; int ret = 0;
/* Call during Seconday interface registration */ /* Call during Seconday interface registration */
if (pdev->id >= SAMSUNG_I2S_SECOFF) { samsung_dai_type = samsung_i2s_get_driver_data(pdev);
if (samsung_dai_type == TYPE_SEC) {
sec_dai = dev_get_drvdata(&pdev->dev); sec_dai = dev_get_drvdata(&pdev->dev);
snd_soc_register_dai(&sec_dai->pdev->dev, snd_soc_register_dai(&sec_dai->pdev->dev,
&sec_dai->i2s_dai_drv); &sec_dai->i2s_dai_drv);
...@@ -1012,31 +1113,60 @@ static int samsung_i2s_probe(struct platform_device *pdev) ...@@ -1012,31 +1113,60 @@ static int samsung_i2s_probe(struct platform_device *pdev)
return 0; return 0;
} }
i2s_pdata = pdev->dev.platform_data; pri_dai = i2s_alloc_dai(pdev, false);
if (i2s_pdata == NULL) { if (!pri_dai) {
dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
return -EINVAL; return -ENOMEM;
} }
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (!np) {
if (!res) { res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); if (!res) {
return -ENXIO; dev_err(&pdev->dev,
} "Unable to get I2S-TX dma resource\n");
dma_pl_chan = res->start; return -ENXIO;
}
pri_dai->dma_playback.channel = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 1); res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
if (!res) { if (!res) {
dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); dev_err(&pdev->dev,
return -ENXIO; "Unable to get I2S-RX dma resource\n");
} return -ENXIO;
dma_cp_chan = res->start; }
pri_dai->dma_capture.channel = res->start;
res = platform_get_resource(pdev, IORESOURCE_DMA, 2); if (i2s_pdata == NULL) {
if (res) dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n");
dma_pl_sec_chan = res->start; return -EINVAL;
else }
dma_pl_sec_chan = 0;
if (&i2s_pdata->type)
i2s_cfg = &i2s_pdata->type.i2s;
if (i2s_cfg) {
quirks = i2s_cfg->quirks;
idma_addr = i2s_cfg->idma_addr;
}
} else {
if (of_find_property(np, "samsung,supports-6ch", NULL))
quirks |= QUIRK_PRI_6CHAN;
if (of_find_property(np, "samsung,supports-secdai", NULL))
quirks |= QUIRK_SEC_DAI;
if (of_find_property(np, "samsung,supports-rstclr", NULL))
quirks |= QUIRK_NEED_RSTCLR;
if (of_property_read_u32(np, "samsung,idma-addr",
&idma_addr)) {
if (quirks & QUIRK_SEC_DAI) {
dev_err(&pdev->dev, "idma address is not"\
"specified");
return -EINVAL;
}
}
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { if (!res) {
...@@ -1051,24 +1181,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) ...@@ -1051,24 +1181,14 @@ static int samsung_i2s_probe(struct platform_device *pdev)
} }
regs_base = res->start; regs_base = res->start;
i2s_cfg = &i2s_pdata->type.i2s;
quirks = i2s_cfg->quirks;
pri_dai = i2s_alloc_dai(pdev, false);
if (!pri_dai) {
dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
ret = -ENOMEM;
goto err;
}
pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
pri_dai->dma_playback.client = pri_dai->dma_playback.client =
(struct s3c2410_dma_client *)&pri_dai->dma_playback; (struct s3c2410_dma_client *)&pri_dai->dma_playback;
pri_dai->dma_playback.ch_name = "tx";
pri_dai->dma_capture.client = pri_dai->dma_capture.client =
(struct s3c2410_dma_client *)&pri_dai->dma_capture; (struct s3c2410_dma_client *)&pri_dai->dma_capture;
pri_dai->dma_playback.channel = dma_pl_chan; pri_dai->dma_capture.ch_name = "rx";
pri_dai->dma_capture.channel = dma_cp_chan;
pri_dai->dma_playback.dma_size = 4; pri_dai->dma_playback.dma_size = 4;
pri_dai->dma_capture.dma_size = 4; pri_dai->dma_capture.dma_size = 4;
pri_dai->base = regs_base; pri_dai->base = regs_base;
...@@ -1087,20 +1207,34 @@ static int samsung_i2s_probe(struct platform_device *pdev) ...@@ -1087,20 +1207,34 @@ static int samsung_i2s_probe(struct platform_device *pdev)
sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
sec_dai->dma_playback.client = sec_dai->dma_playback.client =
(struct s3c2410_dma_client *)&sec_dai->dma_playback; (struct s3c2410_dma_client *)&sec_dai->dma_playback;
/* Use iDMA always if SysDMA not provided */ sec_dai->dma_playback.ch_name = "tx-sec";
sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1;
if (!np) {
res = platform_get_resource(pdev, IORESOURCE_DMA, 2);
if (res)
sec_dai->dma_playback.channel = res->start;
}
sec_dai->dma_playback.dma_size = 4; sec_dai->dma_playback.dma_size = 4;
sec_dai->base = regs_base; sec_dai->base = regs_base;
sec_dai->quirks = quirks; sec_dai->quirks = quirks;
sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr; sec_dai->idma_playback.dma_addr = idma_addr;
sec_dai->pri_dai = pri_dai; sec_dai->pri_dai = pri_dai;
pri_dai->sec_dai = sec_dai; pri_dai->sec_dai = sec_dai;
} }
if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { if (np) {
dev_err(&pdev->dev, "Unable to configure gpio\n"); if (samsung_i2s_parse_dt_gpio(pri_dai)) {
ret = -EINVAL; dev_err(&pdev->dev, "Unable to configure gpio\n");
goto err; ret = -EINVAL;
goto err;
}
} else {
if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to configure gpio\n");
ret = -EINVAL;
goto err;
}
} }
snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv); snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv);
...@@ -1120,10 +1254,14 @@ static int samsung_i2s_remove(struct platform_device *pdev) ...@@ -1120,10 +1254,14 @@ static int samsung_i2s_remove(struct platform_device *pdev)
{ {
struct i2s_dai *i2s, *other; struct i2s_dai *i2s, *other;
struct resource *res; struct resource *res;
struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data;
i2s = dev_get_drvdata(&pdev->dev); i2s = dev_get_drvdata(&pdev->dev);
other = i2s->pri_dai ? : i2s->sec_dai; other = i2s->pri_dai ? : i2s->sec_dai;
if (!i2s_pdata->cfg_gpio && pdev->dev.of_node)
samsung_i2s_dt_gpio_free(i2s->pri_dai);
if (other) { if (other) {
other->pri_dai = NULL; other->pri_dai = NULL;
other->sec_dai = NULL; other->sec_dai = NULL;
...@@ -1143,12 +1281,47 @@ static int samsung_i2s_remove(struct platform_device *pdev) ...@@ -1143,12 +1281,47 @@ static int samsung_i2s_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_device_id samsung_i2s_driver_ids[] = {
{
.name = "samsung-i2s",
.driver_data = TYPE_PRI,
}, {
.name = "samsung-i2s-sec",
.driver_data = TYPE_SEC,
},
{},
};
MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids);
#ifdef CONFIG_OF
static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = {
[TYPE_PRI] = { TYPE_PRI },
[TYPE_SEC] = { TYPE_SEC },
};
static const struct of_device_id exynos_i2s_match[] = {
{ .compatible = "samsung,i2s-v5",
.data = &samsung_i2s_dai_data_array[TYPE_PRI],
},
{},
};
MODULE_DEVICE_TABLE(of, exynos_i2s_match);
#endif
static const struct dev_pm_ops samsung_i2s_pm = {
SET_RUNTIME_PM_OPS(i2s_runtime_suspend,
i2s_runtime_resume, NULL)
};
static struct platform_driver samsung_i2s_driver = { static struct platform_driver samsung_i2s_driver = {
.probe = samsung_i2s_probe, .probe = samsung_i2s_probe,
.remove = samsung_i2s_remove, .remove = samsung_i2s_remove,
.id_table = samsung_i2s_driver_ids,
.driver = { .driver = {
.name = "samsung-i2s", .name = "samsung-i2s",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(exynos_i2s_match),
.pm = &samsung_i2s_pm,
}, },
}; };
......
...@@ -13,13 +13,6 @@ ...@@ -13,13 +13,6 @@
#ifndef __SND_SOC_SAMSUNG_I2S_H #ifndef __SND_SOC_SAMSUNG_I2S_H
#define __SND_SOC_SAMSUNG_I2S_H #define __SND_SOC_SAMSUNG_I2S_H
/*
* Maximum number of I2S blocks that any SoC can have.
* The secondary interface of a CPU dai(if there exists any),
* is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF]
*/
#define SAMSUNG_I2S_SECOFF 4
#define SAMSUNG_I2S_DIV_BCLK 1 #define SAMSUNG_I2S_DIV_BCLK 1
#define SAMSUNG_I2S_RCLKSRC_0 0 #define SAMSUNG_I2S_RCLKSRC_0 0
......
...@@ -193,9 +193,9 @@ static struct snd_soc_dai_link smdk_dai[] = { ...@@ -193,9 +193,9 @@ static struct snd_soc_dai_link smdk_dai[] = {
[SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */ [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
.name = "Sec_FIFO TX", .name = "Sec_FIFO TX",
.stream_name = "Playback", .stream_name = "Playback",
.cpu_dai_name = "samsung-i2s.x", .cpu_dai_name = "samsung-i2s-sec",
.codec_dai_name = "wm8580-hifi-playback", .codec_dai_name = "wm8580-hifi-playback",
.platform_name = "samsung-i2s.x", .platform_name = "samsung-i2s-sec",
.codec_name = "wm8580.0-001b", .codec_name = "wm8580.0-001b",
.ops = &smdk_ops, .ops = &smdk_ops,
}, },
...@@ -223,9 +223,6 @@ static int __init smdk_audio_init(void) ...@@ -223,9 +223,6 @@ static int __init smdk_audio_init(void)
if (machine_is_smdkc100() if (machine_is_smdkc100()
|| machine_is_smdkv210() || machine_is_smdkc110()) { || machine_is_smdkv210() || machine_is_smdkc110()) {
smdk.num_links = 3; smdk.num_links = 3;
/* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */
str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name;
str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF;
} else if (machine_is_smdk6410()) { } else if (machine_is_smdk6410()) {
str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name; str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
str[strlen(str) - 1] = '2'; str[strlen(str) - 1] = '2';
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "../codecs/wm8994.h" #include "../codecs/wm8994.h"
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
/* /*
* Default CFG switch settings to use this driver: * Default CFG switch settings to use this driver:
...@@ -134,9 +135,9 @@ static struct snd_soc_dai_link smdk_dai[] = { ...@@ -134,9 +135,9 @@ static struct snd_soc_dai_link smdk_dai[] = {
}, { /* Sec_Fifo Playback i/f */ }, { /* Sec_Fifo Playback i/f */
.name = "Sec_FIFO TX", .name = "Sec_FIFO TX",
.stream_name = "Sec_Dai", .stream_name = "Sec_Dai",
.cpu_dai_name = "samsung-i2s.4", .cpu_dai_name = "samsung-i2s-sec",
.codec_dai_name = "wm8994-aif1", .codec_dai_name = "wm8994-aif1",
.platform_name = "samsung-i2s.4", .platform_name = "samsung-i2s-sec",
.codec_name = "wm8994-codec", .codec_name = "wm8994-codec",
.ops = &smdk_ops, .ops = &smdk_ops,
}, },
...@@ -153,9 +154,25 @@ static struct snd_soc_card smdk = { ...@@ -153,9 +154,25 @@ static struct snd_soc_card smdk = {
static int smdk_audio_probe(struct platform_device *pdev) static int smdk_audio_probe(struct platform_device *pdev)
{ {
int ret; int ret;
struct device_node *np = pdev->dev.of_node;
struct snd_soc_card *card = &smdk; struct snd_soc_card *card = &smdk;
card->dev = &pdev->dev; card->dev = &pdev->dev;
if (np) {
smdk_dai[0].cpu_dai_name = NULL;
smdk_dai[0].cpu_of_node = of_parse_phandle(np,
"samsung,i2s-controller", 0);
if (!smdk_dai[0].cpu_of_node) {
dev_err(&pdev->dev,
"Property 'samsung,i2s-controller' missing or invalid\n");
ret = -EINVAL;
}
smdk_dai[0].platform_name = NULL;
smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
}
ret = snd_soc_register_card(card); ret = snd_soc_register_card(card);
if (ret) if (ret)
...@@ -173,10 +190,19 @@ static int smdk_audio_remove(struct platform_device *pdev) ...@@ -173,10 +190,19 @@ static int smdk_audio_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id samsung_wm8994_of_match[] = {
{ .compatible = "samsung,smdk-wm8994", },
{},
};
MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
#endif /* CONFIG_OF */
static struct platform_driver smdk_audio_driver = { static struct platform_driver smdk_audio_driver = {
.driver = { .driver = {
.name = "smdk-audio", .name = "smdk-audio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_wm8994_of_match),
}, },
.probe = smdk_audio_probe, .probe = smdk_audio_probe,
.remove = smdk_audio_remove, .remove = smdk_audio_remove,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment