diff options
Diffstat (limited to 'sound/soc/sh')
-rw-r--r-- | sound/soc/sh/Kconfig | 25 | ||||
-rw-r--r-- | sound/soc/sh/Makefile | 6 | ||||
-rw-r--r-- | sound/soc/sh/dma-sh7760.c | 1 | ||||
-rw-r--r-- | sound/soc/sh/fsi-ak4642.c | 30 | ||||
-rw-r--r-- | sound/soc/sh/fsi-da7210.c | 83 | ||||
-rw-r--r-- | sound/soc/sh/fsi.c | 483 | ||||
-rw-r--r-- | sound/soc/sh/migor.c | 218 | ||||
-rw-r--r-- | sound/soc/sh/siu.h | 193 | ||||
-rw-r--r-- | sound/soc/sh/siu_dai.c | 848 | ||||
-rw-r--r-- | sound/soc/sh/siu_pcm.c | 615 |
10 files changed, 2255 insertions, 247 deletions
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 9154b4363db3..f07f6d8b93e1 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
@@ -23,10 +23,17 @@ config SND_SOC_SH4_SSI | |||
23 | config SND_SOC_SH4_FSI | 23 | config SND_SOC_SH4_FSI |
24 | tristate "SH4 FSI support" | 24 | tristate "SH4 FSI support" |
25 | depends on CPU_SUBTYPE_SH7724 | 25 | depends on CPU_SUBTYPE_SH7724 |
26 | select SH_DMA | ||
27 | help | 26 | help |
28 | This option enables FSI sound support | 27 | This option enables FSI sound support |
29 | 28 | ||
29 | config SND_SOC_SH4_SIU | ||
30 | tristate | ||
31 | depends on (SUPERH || ARCH_SHMOBILE) && HAVE_CLK | ||
32 | select DMA_ENGINE | ||
33 | select DMADEVICES | ||
34 | select SH_DMAE | ||
35 | select FW_LOADER | ||
36 | |||
30 | ## | 37 | ## |
31 | ## Boards | 38 | ## Boards |
32 | ## | 39 | ## |
@@ -48,4 +55,20 @@ config SND_FSI_AK4642 | |||
48 | This option enables generic sound support for the | 55 | This option enables generic sound support for the |
49 | FSI - AK4642 unit | 56 | FSI - AK4642 unit |
50 | 57 | ||
58 | config SND_FSI_DA7210 | ||
59 | bool "FSI-DA7210 sound support" | ||
60 | depends on SND_SOC_SH4_FSI | ||
61 | select SND_SOC_DA7210 | ||
62 | help | ||
63 | This option enables generic sound support for the | ||
64 | FSI - DA7210 unit | ||
65 | |||
66 | config SND_SIU_MIGOR | ||
67 | tristate "SIU sound support on Migo-R" | ||
68 | depends on SH_MIGOR | ||
69 | select SND_SOC_SH4_SIU | ||
70 | select SND_SOC_WM8978 | ||
71 | help | ||
72 | This option enables sound support for the SH7722 Migo-R board | ||
73 | |||
51 | endmenu | 74 | endmenu |
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index a6997872f24e..8a5a19293bda 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile | |||
@@ -6,13 +6,19 @@ obj-$(CONFIG_SND_SOC_PCM_SH7760) += snd-soc-dma-sh7760.o | |||
6 | snd-soc-hac-objs := hac.o | 6 | snd-soc-hac-objs := hac.o |
7 | snd-soc-ssi-objs := ssi.o | 7 | snd-soc-ssi-objs := ssi.o |
8 | snd-soc-fsi-objs := fsi.o | 8 | snd-soc-fsi-objs := fsi.o |
9 | snd-soc-siu-objs := siu_pcm.o siu_dai.o | ||
9 | obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o | 10 | obj-$(CONFIG_SND_SOC_SH4_HAC) += snd-soc-hac.o |
10 | obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o | 11 | obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o |
11 | obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o | 12 | obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o |
13 | obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o | ||
12 | 14 | ||
13 | ## boards | 15 | ## boards |
14 | snd-soc-sh7760-ac97-objs := sh7760-ac97.o | 16 | snd-soc-sh7760-ac97-objs := sh7760-ac97.o |
15 | snd-soc-fsi-ak4642-objs := fsi-ak4642.o | 17 | snd-soc-fsi-ak4642-objs := fsi-ak4642.o |
18 | snd-soc-fsi-da7210-objs := fsi-da7210.o | ||
19 | snd-soc-migor-objs := migor.o | ||
16 | 20 | ||
17 | obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o | 21 | obj-$(CONFIG_SND_SH7760_AC97) += snd-soc-sh7760-ac97.o |
18 | obj-$(CONFIG_SND_FSI_AK4642) += snd-soc-fsi-ak4642.o | 22 | obj-$(CONFIG_SND_FSI_AK4642) += snd-soc-fsi-ak4642.o |
23 | obj-$(CONFIG_SND_FSI_DA7210) += snd-soc-fsi-da7210.o | ||
24 | obj-$(CONFIG_SND_SIU_MIGOR) += snd-soc-migor.o | ||
diff --git a/sound/soc/sh/dma-sh7760.c b/sound/soc/sh/dma-sh7760.c index baddb1242c71..0d8bdf07729c 100644 --- a/sound/soc/sh/dma-sh7760.c +++ b/sound/soc/sh/dma-sh7760.c | |||
@@ -13,6 +13,7 @@ | |||
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/gfp.h> | ||
16 | #include <linux/init.h> | 17 | #include <linux/init.h> |
17 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
18 | #include <linux/dma-mapping.h> | 19 | #include <linux/dma-mapping.h> |
diff --git a/sound/soc/sh/fsi-ak4642.c b/sound/soc/sh/fsi-ak4642.c index c7af09729c6e..5263ab18f827 100644 --- a/sound/soc/sh/fsi-ak4642.c +++ b/sound/soc/sh/fsi-ak4642.c | |||
@@ -42,42 +42,12 @@ static struct snd_soc_device fsi_snd_devdata = { | |||
42 | .codec_dev = &soc_codec_dev_ak4642, | 42 | .codec_dev = &soc_codec_dev_ak4642, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | #define AK4642_BUS 0 | ||
46 | #define AK4642_ADR 0x12 | ||
47 | static int ak4642_add_i2c_device(void) | ||
48 | { | ||
49 | struct i2c_board_info info; | ||
50 | struct i2c_adapter *adapter; | ||
51 | struct i2c_client *client; | ||
52 | |||
53 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
54 | info.addr = AK4642_ADR; | ||
55 | strlcpy(info.type, "ak4642", I2C_NAME_SIZE); | ||
56 | |||
57 | adapter = i2c_get_adapter(AK4642_BUS); | ||
58 | if (!adapter) { | ||
59 | printk(KERN_DEBUG "can't get i2c adapter\n"); | ||
60 | return -ENODEV; | ||
61 | } | ||
62 | |||
63 | client = i2c_new_device(adapter, &info); | ||
64 | i2c_put_adapter(adapter); | ||
65 | if (!client) { | ||
66 | printk(KERN_DEBUG "can't add i2c device\n"); | ||
67 | return -ENODEV; | ||
68 | } | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static struct platform_device *fsi_snd_device; | 45 | static struct platform_device *fsi_snd_device; |
74 | 46 | ||
75 | static int __init fsi_ak4642_init(void) | 47 | static int __init fsi_ak4642_init(void) |
76 | { | 48 | { |
77 | int ret = -ENOMEM; | 49 | int ret = -ENOMEM; |
78 | 50 | ||
79 | ak4642_add_i2c_device(); | ||
80 | |||
81 | fsi_snd_device = platform_device_alloc("soc-audio", -1); | 51 | fsi_snd_device = platform_device_alloc("soc-audio", -1); |
82 | if (!fsi_snd_device) | 52 | if (!fsi_snd_device) |
83 | goto out; | 53 | goto out; |
diff --git a/sound/soc/sh/fsi-da7210.c b/sound/soc/sh/fsi-da7210.c new file mode 100644 index 000000000000..33b4d177f466 --- /dev/null +++ b/sound/soc/sh/fsi-da7210.c | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | * fsi-da7210.c | ||
3 | * | ||
4 | * Copyright (C) 2009 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License as published by the | ||
9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
10 | * option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/io.h> | ||
16 | #include <linux/i2c.h> | ||
17 | #include <sound/core.h> | ||
18 | #include <sound/pcm.h> | ||
19 | #include <sound/pcm_params.h> | ||
20 | #include <sound/soc.h> | ||
21 | #include <sound/soc-dapm.h> | ||
22 | |||
23 | #include <sound/sh_fsi.h> | ||
24 | #include "../codecs/da7210.h" | ||
25 | |||
26 | static int fsi_da7210_init(struct snd_soc_codec *codec) | ||
27 | { | ||
28 | return snd_soc_dai_set_fmt(&da7210_dai, | ||
29 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
30 | SND_SOC_DAIFMT_CBM_CFM); | ||
31 | } | ||
32 | |||
33 | static struct snd_soc_dai_link fsi_da7210_dai = { | ||
34 | .name = "DA7210", | ||
35 | .stream_name = "DA7210", | ||
36 | .cpu_dai = &fsi_soc_dai[1], /* FSI B */ | ||
37 | .codec_dai = &da7210_dai, | ||
38 | .init = fsi_da7210_init, | ||
39 | }; | ||
40 | |||
41 | static struct snd_soc_card fsi_soc_card = { | ||
42 | .name = "FSI", | ||
43 | .platform = &fsi_soc_platform, | ||
44 | .dai_link = &fsi_da7210_dai, | ||
45 | .num_links = 1, | ||
46 | }; | ||
47 | |||
48 | static struct snd_soc_device fsi_da7210_snd_devdata = { | ||
49 | .card = &fsi_soc_card, | ||
50 | .codec_dev = &soc_codec_dev_da7210, | ||
51 | }; | ||
52 | |||
53 | static struct platform_device *fsi_da7210_snd_device; | ||
54 | |||
55 | static int __init fsi_da7210_sound_init(void) | ||
56 | { | ||
57 | int ret; | ||
58 | |||
59 | fsi_da7210_snd_device = platform_device_alloc("soc-audio", -1); | ||
60 | if (!fsi_da7210_snd_device) | ||
61 | return -ENOMEM; | ||
62 | |||
63 | platform_set_drvdata(fsi_da7210_snd_device, &fsi_da7210_snd_devdata); | ||
64 | fsi_da7210_snd_devdata.dev = &fsi_da7210_snd_device->dev; | ||
65 | ret = platform_device_add(fsi_da7210_snd_device); | ||
66 | if (ret) | ||
67 | platform_device_put(fsi_da7210_snd_device); | ||
68 | |||
69 | return ret; | ||
70 | } | ||
71 | |||
72 | static void __exit fsi_da7210_sound_exit(void) | ||
73 | { | ||
74 | platform_device_unregister(fsi_da7210_snd_device); | ||
75 | } | ||
76 | |||
77 | module_init(fsi_da7210_sound_init); | ||
78 | module_exit(fsi_da7210_sound_exit); | ||
79 | |||
80 | /* Module information */ | ||
81 | MODULE_DESCRIPTION("ALSA SoC FSI DA2710"); | ||
82 | MODULE_AUTHOR("Kuninori Morimoto <morimoto.kuninori@renesas.com>"); | ||
83 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index 44123248b630..8dc966f45c36 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c | |||
@@ -17,8 +17,9 @@ | |||
17 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/list.h> | 19 | #include <linux/list.h> |
20 | #include <linux/clk.h> | 20 | #include <linux/pm_runtime.h> |
21 | #include <linux/io.h> | 21 | #include <linux/io.h> |
22 | #include <linux/slab.h> | ||
22 | #include <sound/core.h> | 23 | #include <sound/core.h> |
23 | #include <sound/pcm.h> | 24 | #include <sound/pcm.h> |
24 | #include <sound/initval.h> | 25 | #include <sound/initval.h> |
@@ -26,8 +27,6 @@ | |||
26 | #include <sound/pcm_params.h> | 27 | #include <sound/pcm_params.h> |
27 | #include <sound/sh_fsi.h> | 28 | #include <sound/sh_fsi.h> |
28 | #include <asm/atomic.h> | 29 | #include <asm/atomic.h> |
29 | #include <asm/dma.h> | ||
30 | #include <asm/dma-sh.h> | ||
31 | 30 | ||
32 | #define DO_FMT 0x0000 | 31 | #define DO_FMT 0x0000 |
33 | #define DOFF_CTL 0x0004 | 32 | #define DOFF_CTL 0x0004 |
@@ -69,6 +68,7 @@ | |||
69 | /* DOFF_ST */ | 68 | /* DOFF_ST */ |
70 | #define ERR_OVER 0x00000010 | 69 | #define ERR_OVER 0x00000010 |
71 | #define ERR_UNDER 0x00000001 | 70 | #define ERR_UNDER 0x00000001 |
71 | #define ST_ERR (ERR_OVER | ERR_UNDER) | ||
72 | 72 | ||
73 | /* CLK_RST */ | 73 | /* CLK_RST */ |
74 | #define B_CLK 0x00000010 | 74 | #define B_CLK 0x00000010 |
@@ -94,10 +94,10 @@ | |||
94 | struct fsi_priv { | 94 | struct fsi_priv { |
95 | void __iomem *base; | 95 | void __iomem *base; |
96 | struct snd_pcm_substream *substream; | 96 | struct snd_pcm_substream *substream; |
97 | struct fsi_master *master; | ||
97 | 98 | ||
98 | int fifo_max; | 99 | int fifo_max; |
99 | int chan; | 100 | int chan; |
100 | int dma_chan; | ||
101 | 101 | ||
102 | int byte_offset; | 102 | int byte_offset; |
103 | int period_len; | 103 | int period_len; |
@@ -108,14 +108,12 @@ struct fsi_priv { | |||
108 | struct fsi_master { | 108 | struct fsi_master { |
109 | void __iomem *base; | 109 | void __iomem *base; |
110 | int irq; | 110 | int irq; |
111 | struct clk *clk; | ||
112 | struct fsi_priv fsia; | 111 | struct fsi_priv fsia; |
113 | struct fsi_priv fsib; | 112 | struct fsi_priv fsib; |
114 | struct sh_fsi_platform_info *info; | 113 | struct sh_fsi_platform_info *info; |
114 | spinlock_t lock; | ||
115 | }; | 115 | }; |
116 | 116 | ||
117 | static struct fsi_master *master; | ||
118 | |||
119 | /************************************************************************ | 117 | /************************************************************************ |
120 | 118 | ||
121 | 119 | ||
@@ -123,35 +121,35 @@ static struct fsi_master *master; | |||
123 | 121 | ||
124 | 122 | ||
125 | ************************************************************************/ | 123 | ************************************************************************/ |
126 | static int __fsi_reg_write(u32 reg, u32 data) | 124 | static void __fsi_reg_write(u32 reg, u32 data) |
127 | { | 125 | { |
128 | /* valid data area is 24bit */ | 126 | /* valid data area is 24bit */ |
129 | data &= 0x00ffffff; | 127 | data &= 0x00ffffff; |
130 | 128 | ||
131 | return ctrl_outl(data, reg); | 129 | __raw_writel(data, reg); |
132 | } | 130 | } |
133 | 131 | ||
134 | static u32 __fsi_reg_read(u32 reg) | 132 | static u32 __fsi_reg_read(u32 reg) |
135 | { | 133 | { |
136 | return ctrl_inl(reg); | 134 | return __raw_readl(reg); |
137 | } | 135 | } |
138 | 136 | ||
139 | static int __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) | 137 | static void __fsi_reg_mask_set(u32 reg, u32 mask, u32 data) |
140 | { | 138 | { |
141 | u32 val = __fsi_reg_read(reg); | 139 | u32 val = __fsi_reg_read(reg); |
142 | 140 | ||
143 | val &= ~mask; | 141 | val &= ~mask; |
144 | val |= data & mask; | 142 | val |= data & mask; |
145 | 143 | ||
146 | return __fsi_reg_write(reg, val); | 144 | __fsi_reg_write(reg, val); |
147 | } | 145 | } |
148 | 146 | ||
149 | static int fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) | 147 | static void fsi_reg_write(struct fsi_priv *fsi, u32 reg, u32 data) |
150 | { | 148 | { |
151 | if (reg > REG_END) | 149 | if (reg > REG_END) |
152 | return -1; | 150 | return; |
153 | 151 | ||
154 | return __fsi_reg_write((u32)(fsi->base + reg), data); | 152 | __fsi_reg_write((u32)(fsi->base + reg), data); |
155 | } | 153 | } |
156 | 154 | ||
157 | static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) | 155 | static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) |
@@ -162,39 +160,55 @@ static u32 fsi_reg_read(struct fsi_priv *fsi, u32 reg) | |||
162 | return __fsi_reg_read((u32)(fsi->base + reg)); | 160 | return __fsi_reg_read((u32)(fsi->base + reg)); |
163 | } | 161 | } |
164 | 162 | ||
165 | static int fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) | 163 | static void fsi_reg_mask_set(struct fsi_priv *fsi, u32 reg, u32 mask, u32 data) |
166 | { | 164 | { |
167 | if (reg > REG_END) | 165 | if (reg > REG_END) |
168 | return -1; | 166 | return; |
169 | 167 | ||
170 | return __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); | 168 | __fsi_reg_mask_set((u32)(fsi->base + reg), mask, data); |
171 | } | 169 | } |
172 | 170 | ||
173 | static int fsi_master_write(u32 reg, u32 data) | 171 | static void fsi_master_write(struct fsi_master *master, u32 reg, u32 data) |
174 | { | 172 | { |
173 | unsigned long flags; | ||
174 | |||
175 | if ((reg < MREG_START) || | 175 | if ((reg < MREG_START) || |
176 | (reg > MREG_END)) | 176 | (reg > MREG_END)) |
177 | return -1; | 177 | return; |
178 | 178 | ||
179 | return __fsi_reg_write((u32)(master->base + reg), data); | 179 | spin_lock_irqsave(&master->lock, flags); |
180 | __fsi_reg_write((u32)(master->base + reg), data); | ||
181 | spin_unlock_irqrestore(&master->lock, flags); | ||
180 | } | 182 | } |
181 | 183 | ||
182 | static u32 fsi_master_read(u32 reg) | 184 | static u32 fsi_master_read(struct fsi_master *master, u32 reg) |
183 | { | 185 | { |
186 | u32 ret; | ||
187 | unsigned long flags; | ||
188 | |||
184 | if ((reg < MREG_START) || | 189 | if ((reg < MREG_START) || |
185 | (reg > MREG_END)) | 190 | (reg > MREG_END)) |
186 | return 0; | 191 | return 0; |
187 | 192 | ||
188 | return __fsi_reg_read((u32)(master->base + reg)); | 193 | spin_lock_irqsave(&master->lock, flags); |
194 | ret = __fsi_reg_read((u32)(master->base + reg)); | ||
195 | spin_unlock_irqrestore(&master->lock, flags); | ||
196 | |||
197 | return ret; | ||
189 | } | 198 | } |
190 | 199 | ||
191 | static int fsi_master_mask_set(u32 reg, u32 mask, u32 data) | 200 | static void fsi_master_mask_set(struct fsi_master *master, |
201 | u32 reg, u32 mask, u32 data) | ||
192 | { | 202 | { |
203 | unsigned long flags; | ||
204 | |||
193 | if ((reg < MREG_START) || | 205 | if ((reg < MREG_START) || |
194 | (reg > MREG_END)) | 206 | (reg > MREG_END)) |
195 | return -1; | 207 | return; |
196 | 208 | ||
197 | return __fsi_reg_mask_set((u32)(master->base + reg), mask, data); | 209 | spin_lock_irqsave(&master->lock, flags); |
210 | __fsi_reg_mask_set((u32)(master->base + reg), mask, data); | ||
211 | spin_unlock_irqrestore(&master->lock, flags); | ||
198 | } | 212 | } |
199 | 213 | ||
200 | /************************************************************************ | 214 | /************************************************************************ |
@@ -204,43 +218,35 @@ static int fsi_master_mask_set(u32 reg, u32 mask, u32 data) | |||
204 | 218 | ||
205 | 219 | ||
206 | ************************************************************************/ | 220 | ************************************************************************/ |
207 | static struct fsi_priv *fsi_get(struct snd_pcm_substream *substream) | 221 | static struct fsi_master *fsi_get_master(struct fsi_priv *fsi) |
208 | { | 222 | { |
209 | struct snd_soc_pcm_runtime *rtd; | 223 | return fsi->master; |
210 | struct fsi_priv *fsi = NULL; | 224 | } |
211 | 225 | ||
212 | if (!substream || !master) | 226 | static int fsi_is_port_a(struct fsi_priv *fsi) |
213 | return NULL; | 227 | { |
228 | return fsi->master->base == fsi->base; | ||
229 | } | ||
214 | 230 | ||
215 | rtd = substream->private_data; | 231 | static struct snd_soc_dai *fsi_get_dai(struct snd_pcm_substream *substream) |
216 | switch (rtd->dai->cpu_dai->id) { | 232 | { |
217 | case 0: | 233 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
218 | fsi = &master->fsia; | 234 | struct snd_soc_dai_link *machine = rtd->dai; |
219 | break; | ||
220 | case 1: | ||
221 | fsi = &master->fsib; | ||
222 | break; | ||
223 | } | ||
224 | 235 | ||
225 | return fsi; | 236 | return machine->cpu_dai; |
226 | } | 237 | } |
227 | 238 | ||
228 | static int fsi_is_port_a(struct fsi_priv *fsi) | 239 | static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) |
229 | { | 240 | { |
230 | /* return | 241 | struct snd_soc_dai *dai = fsi_get_dai(substream); |
231 | * 1 : port a | ||
232 | * 0 : port b | ||
233 | */ | ||
234 | |||
235 | if (fsi == &master->fsia) | ||
236 | return 1; | ||
237 | 242 | ||
238 | return 0; | 243 | return dai->private_data; |
239 | } | 244 | } |
240 | 245 | ||
241 | static u32 fsi_get_info_flags(struct fsi_priv *fsi) | 246 | static u32 fsi_get_info_flags(struct fsi_priv *fsi) |
242 | { | 247 | { |
243 | int is_porta = fsi_is_port_a(fsi); | 248 | int is_porta = fsi_is_port_a(fsi); |
249 | struct fsi_master *master = fsi_get_master(fsi); | ||
244 | 250 | ||
245 | return is_porta ? master->info->porta_flags : | 251 | return is_porta ? master->info->porta_flags : |
246 | master->info->portb_flags; | 252 | master->info->portb_flags; |
@@ -308,62 +314,6 @@ static int fsi_get_fifo_residue(struct fsi_priv *fsi, int is_play) | |||
308 | return residue; | 314 | return residue; |
309 | } | 315 | } |
310 | 316 | ||
311 | static int fsi_get_residue(struct fsi_priv *fsi, int is_play) | ||
312 | { | ||
313 | int residue; | ||
314 | int width; | ||
315 | struct snd_pcm_runtime *runtime; | ||
316 | |||
317 | runtime = fsi->substream->runtime; | ||
318 | |||
319 | /* get 1 channel data width */ | ||
320 | width = frames_to_bytes(runtime, 1) / fsi->chan; | ||
321 | |||
322 | if (2 == width) | ||
323 | residue = fsi_get_fifo_residue(fsi, is_play); | ||
324 | else | ||
325 | residue = get_dma_residue(fsi->dma_chan); | ||
326 | |||
327 | return residue; | ||
328 | } | ||
329 | |||
330 | /************************************************************************ | ||
331 | |||
332 | |||
333 | basic dma function | ||
334 | |||
335 | |||
336 | ************************************************************************/ | ||
337 | #define PORTA_DMA 0 | ||
338 | #define PORTB_DMA 1 | ||
339 | |||
340 | static int fsi_get_dma_chan(void) | ||
341 | { | ||
342 | if (0 != request_dma(PORTA_DMA, "fsia")) | ||
343 | return -EIO; | ||
344 | |||
345 | if (0 != request_dma(PORTB_DMA, "fsib")) { | ||
346 | free_dma(PORTA_DMA); | ||
347 | return -EIO; | ||
348 | } | ||
349 | |||
350 | master->fsia.dma_chan = PORTA_DMA; | ||
351 | master->fsib.dma_chan = PORTB_DMA; | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static void fsi_free_dma_chan(void) | ||
357 | { | ||
358 | dma_wait_for_completion(PORTA_DMA); | ||
359 | dma_wait_for_completion(PORTB_DMA); | ||
360 | free_dma(PORTA_DMA); | ||
361 | free_dma(PORTB_DMA); | ||
362 | |||
363 | master->fsia.dma_chan = -1; | ||
364 | master->fsib.dma_chan = -1; | ||
365 | } | ||
366 | |||
367 | /************************************************************************ | 317 | /************************************************************************ |
368 | 318 | ||
369 | 319 | ||
@@ -374,27 +324,30 @@ static void fsi_free_dma_chan(void) | |||
374 | static void fsi_irq_enable(struct fsi_priv *fsi, int is_play) | 324 | static void fsi_irq_enable(struct fsi_priv *fsi, int is_play) |
375 | { | 325 | { |
376 | u32 data = fsi_port_ab_io_bit(fsi, is_play); | 326 | u32 data = fsi_port_ab_io_bit(fsi, is_play); |
327 | struct fsi_master *master = fsi_get_master(fsi); | ||
377 | 328 | ||
378 | fsi_master_mask_set(IMSK, data, data); | 329 | fsi_master_mask_set(master, IMSK, data, data); |
379 | fsi_master_mask_set(IEMSK, data, data); | 330 | fsi_master_mask_set(master, IEMSK, data, data); |
380 | } | 331 | } |
381 | 332 | ||
382 | static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) | 333 | static void fsi_irq_disable(struct fsi_priv *fsi, int is_play) |
383 | { | 334 | { |
384 | u32 data = fsi_port_ab_io_bit(fsi, is_play); | 335 | u32 data = fsi_port_ab_io_bit(fsi, is_play); |
336 | struct fsi_master *master = fsi_get_master(fsi); | ||
385 | 337 | ||
386 | fsi_master_mask_set(IMSK, data, 0); | 338 | fsi_master_mask_set(master, IMSK, data, 0); |
387 | fsi_master_mask_set(IEMSK, data, 0); | 339 | fsi_master_mask_set(master, IEMSK, data, 0); |
388 | } | 340 | } |
389 | 341 | ||
390 | static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable) | 342 | static void fsi_clk_ctrl(struct fsi_priv *fsi, int enable) |
391 | { | 343 | { |
392 | u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4); | 344 | u32 val = fsi_is_port_a(fsi) ? (1 << 0) : (1 << 4); |
345 | struct fsi_master *master = fsi_get_master(fsi); | ||
393 | 346 | ||
394 | if (enable) | 347 | if (enable) |
395 | fsi_master_mask_set(CLK_RST, val, val); | 348 | fsi_master_mask_set(master, CLK_RST, val, val); |
396 | else | 349 | else |
397 | fsi_master_mask_set(CLK_RST, val, 0); | 350 | fsi_master_mask_set(master, CLK_RST, val, 0); |
398 | } | 351 | } |
399 | 352 | ||
400 | static void fsi_irq_init(struct fsi_priv *fsi, int is_play) | 353 | static void fsi_irq_init(struct fsi_priv *fsi, int is_play) |
@@ -415,79 +368,46 @@ static void fsi_irq_init(struct fsi_priv *fsi, int is_play) | |||
415 | fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR); | 368 | fsi_reg_mask_set(fsi, ctrl, FIFO_CLR, FIFO_CLR); |
416 | 369 | ||
417 | /* clear interrupt factor */ | 370 | /* clear interrupt factor */ |
418 | fsi_master_mask_set(INT_ST, data, 0); | 371 | fsi_master_mask_set(fsi_get_master(fsi), INT_ST, data, 0); |
419 | } | 372 | } |
420 | 373 | ||
421 | static void fsi_soft_all_reset(void) | 374 | static void fsi_soft_all_reset(struct fsi_master *master) |
422 | { | 375 | { |
423 | u32 status = fsi_master_read(SOFT_RST); | 376 | u32 status = fsi_master_read(master, SOFT_RST); |
424 | 377 | ||
425 | /* port AB reset */ | 378 | /* port AB reset */ |
426 | status &= 0x000000ff; | 379 | status &= 0x000000ff; |
427 | fsi_master_write(SOFT_RST, status); | 380 | fsi_master_write(master, SOFT_RST, status); |
428 | mdelay(10); | 381 | mdelay(10); |
429 | 382 | ||
430 | /* soft reset */ | 383 | /* soft reset */ |
431 | status &= 0x000000f0; | 384 | status &= 0x000000f0; |
432 | fsi_master_write(SOFT_RST, status); | 385 | fsi_master_write(master, SOFT_RST, status); |
433 | status |= 0x00000001; | 386 | status |= 0x00000001; |
434 | fsi_master_write(SOFT_RST, status); | 387 | fsi_master_write(master, SOFT_RST, status); |
435 | mdelay(10); | 388 | mdelay(10); |
436 | } | 389 | } |
437 | 390 | ||
438 | static void fsi_16data_push(struct fsi_priv *fsi, | ||
439 | struct snd_pcm_runtime *runtime, | ||
440 | int send) | ||
441 | { | ||
442 | u16 *dma_start; | ||
443 | u32 snd; | ||
444 | int i; | ||
445 | |||
446 | /* get dma start position for FSI */ | ||
447 | dma_start = (u16 *)runtime->dma_area; | ||
448 | dma_start += fsi->byte_offset / 2; | ||
449 | |||
450 | /* | ||
451 | * soft dma | ||
452 | * FSI can not use DMA when 16bpp | ||
453 | */ | ||
454 | for (i = 0; i < send; i++) { | ||
455 | snd = (u32)dma_start[i]; | ||
456 | fsi_reg_write(fsi, DODT, snd << 8); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | static void fsi_32data_push(struct fsi_priv *fsi, | ||
461 | struct snd_pcm_runtime *runtime, | ||
462 | int send) | ||
463 | { | ||
464 | u32 *dma_start; | ||
465 | |||
466 | /* get dma start position for FSI */ | ||
467 | dma_start = (u32 *)runtime->dma_area; | ||
468 | dma_start += fsi->byte_offset / 4; | ||
469 | |||
470 | dma_wait_for_completion(fsi->dma_chan); | ||
471 | dma_configure_channel(fsi->dma_chan, (SM_INC|0x400|TS_32|TM_BUR)); | ||
472 | dma_write(fsi->dma_chan, (u32)dma_start, | ||
473 | (u32)(fsi->base + DODT), send * 4); | ||
474 | } | ||
475 | |||
476 | /* playback interrupt */ | 391 | /* playback interrupt */ |
477 | static int fsi_data_push(struct fsi_priv *fsi) | 392 | static int fsi_data_push(struct fsi_priv *fsi, int startup) |
478 | { | 393 | { |
479 | struct snd_pcm_runtime *runtime; | 394 | struct snd_pcm_runtime *runtime; |
480 | struct snd_pcm_substream *substream = NULL; | 395 | struct snd_pcm_substream *substream = NULL; |
396 | u32 status; | ||
481 | int send; | 397 | int send; |
482 | int fifo_free; | 398 | int fifo_free; |
483 | int width; | 399 | int width; |
400 | u8 *start; | ||
401 | int i, over_period; | ||
484 | 402 | ||
485 | if (!fsi || | 403 | if (!fsi || |
486 | !fsi->substream || | 404 | !fsi->substream || |
487 | !fsi->substream->runtime) | 405 | !fsi->substream->runtime) |
488 | return -EINVAL; | 406 | return -EINVAL; |
489 | 407 | ||
490 | runtime = fsi->substream->runtime; | 408 | over_period = 0; |
409 | substream = fsi->substream; | ||
410 | runtime = substream->runtime; | ||
491 | 411 | ||
492 | /* FSI FIFO has limit. | 412 | /* FSI FIFO has limit. |
493 | * So, this driver can not send periods data at a time | 413 | * So, this driver can not send periods data at a time |
@@ -495,7 +415,7 @@ static int fsi_data_push(struct fsi_priv *fsi) | |||
495 | if (fsi->byte_offset >= | 415 | if (fsi->byte_offset >= |
496 | fsi->period_len * (fsi->periods + 1)) { | 416 | fsi->period_len * (fsi->periods + 1)) { |
497 | 417 | ||
498 | substream = fsi->substream; | 418 | over_period = 1; |
499 | fsi->periods = (fsi->periods + 1) % runtime->periods; | 419 | fsi->periods = (fsi->periods + 1) % runtime->periods; |
500 | 420 | ||
501 | if (0 == fsi->periods) | 421 | if (0 == fsi->periods) |
@@ -515,18 +435,122 @@ static int fsi_data_push(struct fsi_priv *fsi) | |||
515 | if (fifo_free < send) | 435 | if (fifo_free < send) |
516 | send = fifo_free; | 436 | send = fifo_free; |
517 | 437 | ||
518 | if (2 == width) | 438 | start = runtime->dma_area; |
519 | fsi_16data_push(fsi, runtime, send); | 439 | start += fsi->byte_offset; |
520 | else if (4 == width) | 440 | |
521 | fsi_32data_push(fsi, runtime, send); | 441 | switch (width) { |
522 | else | 442 | case 2: |
443 | for (i = 0; i < send; i++) | ||
444 | fsi_reg_write(fsi, DODT, | ||
445 | ((u32)*((u16 *)start + i) << 8)); | ||
446 | break; | ||
447 | case 4: | ||
448 | for (i = 0; i < send; i++) | ||
449 | fsi_reg_write(fsi, DODT, *((u32 *)start + i)); | ||
450 | break; | ||
451 | default: | ||
523 | return -EINVAL; | 452 | return -EINVAL; |
453 | } | ||
524 | 454 | ||
525 | fsi->byte_offset += send * width; | 455 | fsi->byte_offset += send * width; |
526 | 456 | ||
457 | status = fsi_reg_read(fsi, DOFF_ST); | ||
458 | if (!startup) { | ||
459 | struct snd_soc_dai *dai = fsi_get_dai(substream); | ||
460 | |||
461 | if (status & ERR_OVER) | ||
462 | dev_err(dai->dev, "over run\n"); | ||
463 | if (status & ERR_UNDER) | ||
464 | dev_err(dai->dev, "under run\n"); | ||
465 | } | ||
466 | fsi_reg_write(fsi, DOFF_ST, 0); | ||
467 | |||
527 | fsi_irq_enable(fsi, 1); | 468 | fsi_irq_enable(fsi, 1); |
528 | 469 | ||
529 | if (substream) | 470 | if (over_period) |
471 | snd_pcm_period_elapsed(substream); | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | static int fsi_data_pop(struct fsi_priv *fsi, int startup) | ||
477 | { | ||
478 | struct snd_pcm_runtime *runtime; | ||
479 | struct snd_pcm_substream *substream = NULL; | ||
480 | u32 status; | ||
481 | int free; | ||
482 | int fifo_fill; | ||
483 | int width; | ||
484 | u8 *start; | ||
485 | int i, over_period; | ||
486 | |||
487 | if (!fsi || | ||
488 | !fsi->substream || | ||
489 | !fsi->substream->runtime) | ||
490 | return -EINVAL; | ||
491 | |||
492 | over_period = 0; | ||
493 | substream = fsi->substream; | ||
494 | runtime = substream->runtime; | ||
495 | |||
496 | /* FSI FIFO has limit. | ||
497 | * So, this driver can not send periods data at a time | ||
498 | */ | ||
499 | if (fsi->byte_offset >= | ||
500 | fsi->period_len * (fsi->periods + 1)) { | ||
501 | |||
502 | over_period = 1; | ||
503 | fsi->periods = (fsi->periods + 1) % runtime->periods; | ||
504 | |||
505 | if (0 == fsi->periods) | ||
506 | fsi->byte_offset = 0; | ||
507 | } | ||
508 | |||
509 | /* get 1 channel data width */ | ||
510 | width = frames_to_bytes(runtime, 1) / fsi->chan; | ||
511 | |||
512 | /* get free space for alsa */ | ||
513 | free = (fsi->buffer_len - fsi->byte_offset) / width; | ||
514 | |||
515 | /* get recv size */ | ||
516 | fifo_fill = fsi_get_fifo_residue(fsi, 0); | ||
517 | |||
518 | if (free < fifo_fill) | ||
519 | fifo_fill = free; | ||
520 | |||
521 | start = runtime->dma_area; | ||
522 | start += fsi->byte_offset; | ||
523 | |||
524 | switch (width) { | ||
525 | case 2: | ||
526 | for (i = 0; i < fifo_fill; i++) | ||
527 | *((u16 *)start + i) = | ||
528 | (u16)(fsi_reg_read(fsi, DIDT) >> 8); | ||
529 | break; | ||
530 | case 4: | ||
531 | for (i = 0; i < fifo_fill; i++) | ||
532 | *((u32 *)start + i) = fsi_reg_read(fsi, DIDT); | ||
533 | break; | ||
534 | default: | ||
535 | return -EINVAL; | ||
536 | } | ||
537 | |||
538 | fsi->byte_offset += fifo_fill * width; | ||
539 | |||
540 | status = fsi_reg_read(fsi, DIFF_ST); | ||
541 | if (!startup) { | ||
542 | struct snd_soc_dai *dai = fsi_get_dai(substream); | ||
543 | |||
544 | if (status & ERR_OVER) | ||
545 | dev_err(dai->dev, "over run\n"); | ||
546 | if (status & ERR_UNDER) | ||
547 | dev_err(dai->dev, "under run\n"); | ||
548 | } | ||
549 | fsi_reg_write(fsi, DIFF_ST, 0); | ||
550 | |||
551 | fsi_irq_enable(fsi, 0); | ||
552 | |||
553 | if (over_period) | ||
530 | snd_pcm_period_elapsed(substream); | 554 | snd_pcm_period_elapsed(substream); |
531 | 555 | ||
532 | return 0; | 556 | return 0; |
@@ -534,19 +558,24 @@ static int fsi_data_push(struct fsi_priv *fsi) | |||
534 | 558 | ||
535 | static irqreturn_t fsi_interrupt(int irq, void *data) | 559 | static irqreturn_t fsi_interrupt(int irq, void *data) |
536 | { | 560 | { |
537 | u32 status = fsi_master_read(SOFT_RST) & ~0x00000010; | 561 | struct fsi_master *master = data; |
538 | u32 int_st = fsi_master_read(INT_ST); | 562 | u32 status = fsi_master_read(master, SOFT_RST) & ~0x00000010; |
563 | u32 int_st = fsi_master_read(master, INT_ST); | ||
539 | 564 | ||
540 | /* clear irq status */ | 565 | /* clear irq status */ |
541 | fsi_master_write(SOFT_RST, status); | 566 | fsi_master_write(master, SOFT_RST, status); |
542 | fsi_master_write(SOFT_RST, status | 0x00000010); | 567 | fsi_master_write(master, SOFT_RST, status | 0x00000010); |
543 | 568 | ||
544 | if (int_st & INT_A_OUT) | 569 | if (int_st & INT_A_OUT) |
545 | fsi_data_push(&master->fsia); | 570 | fsi_data_push(&master->fsia, 0); |
546 | if (int_st & INT_B_OUT) | 571 | if (int_st & INT_B_OUT) |
547 | fsi_data_push(&master->fsib); | 572 | fsi_data_push(&master->fsib, 0); |
573 | if (int_st & INT_A_IN) | ||
574 | fsi_data_pop(&master->fsia, 0); | ||
575 | if (int_st & INT_B_IN) | ||
576 | fsi_data_pop(&master->fsib, 0); | ||
548 | 577 | ||
549 | fsi_master_write(INT_ST, 0x0000000); | 578 | fsi_master_write(master, INT_ST, 0x0000000); |
550 | 579 | ||
551 | return IRQ_HANDLED; | 580 | return IRQ_HANDLED; |
552 | } | 581 | } |
@@ -561,7 +590,7 @@ static irqreturn_t fsi_interrupt(int irq, void *data) | |||
561 | static int fsi_dai_startup(struct snd_pcm_substream *substream, | 590 | static int fsi_dai_startup(struct snd_pcm_substream *substream, |
562 | struct snd_soc_dai *dai) | 591 | struct snd_soc_dai *dai) |
563 | { | 592 | { |
564 | struct fsi_priv *fsi = fsi_get(substream); | 593 | struct fsi_priv *fsi = fsi_get_priv(substream); |
565 | const char *msg; | 594 | const char *msg; |
566 | u32 flags = fsi_get_info_flags(fsi); | 595 | u32 flags = fsi_get_info_flags(fsi); |
567 | u32 fmt; | 596 | u32 fmt; |
@@ -571,7 +600,7 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
571 | int is_master; | 600 | int is_master; |
572 | int ret = 0; | 601 | int ret = 0; |
573 | 602 | ||
574 | clk_enable(master->clk); | 603 | pm_runtime_get_sync(dai->dev); |
575 | 604 | ||
576 | /* CKG1 */ | 605 | /* CKG1 */ |
577 | data = is_play ? (1 << 0) : (1 << 4); | 606 | data = is_play ? (1 << 0) : (1 << 4); |
@@ -664,8 +693,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
664 | } | 693 | } |
665 | 694 | ||
666 | fsi_reg_write(fsi, reg, data); | 695 | fsi_reg_write(fsi, reg, data); |
667 | dev_dbg(dai->dev, "use %s format (%d channel) use %d DMAC\n", | ||
668 | msg, fsi->chan, fsi->dma_chan); | ||
669 | 696 | ||
670 | /* | 697 | /* |
671 | * clear clk reset if master mode | 698 | * clear clk reset if master mode |
@@ -682,33 +709,29 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, | |||
682 | static void fsi_dai_shutdown(struct snd_pcm_substream *substream, | 709 | static void fsi_dai_shutdown(struct snd_pcm_substream *substream, |
683 | struct snd_soc_dai *dai) | 710 | struct snd_soc_dai *dai) |
684 | { | 711 | { |
685 | struct fsi_priv *fsi = fsi_get(substream); | 712 | struct fsi_priv *fsi = fsi_get_priv(substream); |
686 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | 713 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
687 | 714 | ||
688 | fsi_irq_disable(fsi, is_play); | 715 | fsi_irq_disable(fsi, is_play); |
689 | fsi_clk_ctrl(fsi, 0); | 716 | fsi_clk_ctrl(fsi, 0); |
690 | 717 | ||
691 | clk_disable(master->clk); | 718 | pm_runtime_put_sync(dai->dev); |
692 | } | 719 | } |
693 | 720 | ||
694 | static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, | 721 | static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, |
695 | struct snd_soc_dai *dai) | 722 | struct snd_soc_dai *dai) |
696 | { | 723 | { |
697 | struct fsi_priv *fsi = fsi_get(substream); | 724 | struct fsi_priv *fsi = fsi_get_priv(substream); |
698 | struct snd_pcm_runtime *runtime = substream->runtime; | 725 | struct snd_pcm_runtime *runtime = substream->runtime; |
699 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | 726 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; |
700 | int ret = 0; | 727 | int ret = 0; |
701 | 728 | ||
702 | /* capture not supported */ | ||
703 | if (!is_play) | ||
704 | return -ENODEV; | ||
705 | |||
706 | switch (cmd) { | 729 | switch (cmd) { |
707 | case SNDRV_PCM_TRIGGER_START: | 730 | case SNDRV_PCM_TRIGGER_START: |
708 | fsi_stream_push(fsi, substream, | 731 | fsi_stream_push(fsi, substream, |
709 | frames_to_bytes(runtime, runtime->buffer_size), | 732 | frames_to_bytes(runtime, runtime->buffer_size), |
710 | frames_to_bytes(runtime, runtime->period_size)); | 733 | frames_to_bytes(runtime, runtime->period_size)); |
711 | ret = fsi_data_push(fsi); | 734 | ret = is_play ? fsi_data_push(fsi, 1) : fsi_data_pop(fsi, 1); |
712 | break; | 735 | break; |
713 | case SNDRV_PCM_TRIGGER_STOP: | 736 | case SNDRV_PCM_TRIGGER_STOP: |
714 | fsi_irq_disable(fsi, is_play); | 737 | fsi_irq_disable(fsi, is_play); |
@@ -779,11 +802,10 @@ static int fsi_hw_free(struct snd_pcm_substream *substream) | |||
779 | static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) | 802 | static snd_pcm_uframes_t fsi_pointer(struct snd_pcm_substream *substream) |
780 | { | 803 | { |
781 | struct snd_pcm_runtime *runtime = substream->runtime; | 804 | struct snd_pcm_runtime *runtime = substream->runtime; |
782 | struct fsi_priv *fsi = fsi_get(substream); | 805 | struct fsi_priv *fsi = fsi_get_priv(substream); |
783 | int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; | ||
784 | long location; | 806 | long location; |
785 | 807 | ||
786 | location = (fsi->byte_offset - 1) - fsi_get_residue(fsi, is_play); | 808 | location = (fsi->byte_offset - 1); |
787 | if (location < 0) | 809 | if (location < 0) |
788 | location = 0; | 810 | location = 0; |
789 | 811 | ||
@@ -845,7 +867,12 @@ struct snd_soc_dai fsi_soc_dai[] = { | |||
845 | .channels_min = 1, | 867 | .channels_min = 1, |
846 | .channels_max = 8, | 868 | .channels_max = 8, |
847 | }, | 869 | }, |
848 | /* capture not supported */ | 870 | .capture = { |
871 | .rates = FSI_RATES, | ||
872 | .formats = FSI_FMTS, | ||
873 | .channels_min = 1, | ||
874 | .channels_max = 8, | ||
875 | }, | ||
849 | .ops = &fsi_dai_ops, | 876 | .ops = &fsi_dai_ops, |
850 | }, | 877 | }, |
851 | { | 878 | { |
@@ -857,7 +884,12 @@ struct snd_soc_dai fsi_soc_dai[] = { | |||
857 | .channels_min = 1, | 884 | .channels_min = 1, |
858 | .channels_max = 8, | 885 | .channels_max = 8, |
859 | }, | 886 | }, |
860 | /* capture not supported */ | 887 | .capture = { |
888 | .rates = FSI_RATES, | ||
889 | .formats = FSI_FMTS, | ||
890 | .channels_min = 1, | ||
891 | .channels_max = 8, | ||
892 | }, | ||
861 | .ops = &fsi_dai_ops, | 893 | .ops = &fsi_dai_ops, |
862 | }, | 894 | }, |
863 | }; | 895 | }; |
@@ -880,14 +912,19 @@ EXPORT_SYMBOL_GPL(fsi_soc_platform); | |||
880 | ************************************************************************/ | 912 | ************************************************************************/ |
881 | static int fsi_probe(struct platform_device *pdev) | 913 | static int fsi_probe(struct platform_device *pdev) |
882 | { | 914 | { |
915 | struct fsi_master *master; | ||
883 | struct resource *res; | 916 | struct resource *res; |
884 | char clk_name[8]; | ||
885 | unsigned int irq; | 917 | unsigned int irq; |
886 | int ret; | 918 | int ret; |
887 | 919 | ||
920 | if (0 != pdev->id) { | ||
921 | dev_err(&pdev->dev, "current fsi support id 0 only now\n"); | ||
922 | return -ENODEV; | ||
923 | } | ||
924 | |||
888 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 925 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
889 | irq = platform_get_irq(pdev, 0); | 926 | irq = platform_get_irq(pdev, 0); |
890 | if (!res || !irq) { | 927 | if (!res || (int)irq <= 0) { |
891 | dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); | 928 | dev_err(&pdev->dev, "Not enough FSI platform resources.\n"); |
892 | ret = -ENODEV; | 929 | ret = -ENODEV; |
893 | goto exit; | 930 | goto exit; |
@@ -910,35 +947,25 @@ static int fsi_probe(struct platform_device *pdev) | |||
910 | master->irq = irq; | 947 | master->irq = irq; |
911 | master->info = pdev->dev.platform_data; | 948 | master->info = pdev->dev.platform_data; |
912 | master->fsia.base = master->base; | 949 | master->fsia.base = master->base; |
950 | master->fsia.master = master; | ||
913 | master->fsib.base = master->base + 0x40; | 951 | master->fsib.base = master->base + 0x40; |
952 | master->fsib.master = master; | ||
953 | spin_lock_init(&master->lock); | ||
914 | 954 | ||
915 | master->fsia.dma_chan = -1; | 955 | pm_runtime_enable(&pdev->dev); |
916 | master->fsib.dma_chan = -1; | 956 | pm_runtime_resume(&pdev->dev); |
917 | |||
918 | ret = fsi_get_dma_chan(); | ||
919 | if (ret < 0) { | ||
920 | dev_err(&pdev->dev, "cannot get dma api\n"); | ||
921 | goto exit_iounmap; | ||
922 | } | ||
923 | |||
924 | /* FSI is based on SPU mstp */ | ||
925 | snprintf(clk_name, sizeof(clk_name), "spu%d", pdev->id); | ||
926 | master->clk = clk_get(NULL, clk_name); | ||
927 | if (IS_ERR(master->clk)) { | ||
928 | dev_err(&pdev->dev, "cannot get %s mstp\n", clk_name); | ||
929 | ret = -EIO; | ||
930 | goto exit_free_dma; | ||
931 | } | ||
932 | 957 | ||
933 | fsi_soc_dai[0].dev = &pdev->dev; | 958 | fsi_soc_dai[0].dev = &pdev->dev; |
959 | fsi_soc_dai[0].private_data = &master->fsia; | ||
934 | fsi_soc_dai[1].dev = &pdev->dev; | 960 | fsi_soc_dai[1].dev = &pdev->dev; |
961 | fsi_soc_dai[1].private_data = &master->fsib; | ||
935 | 962 | ||
936 | fsi_soft_all_reset(); | 963 | fsi_soft_all_reset(master); |
937 | 964 | ||
938 | ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master); | 965 | ret = request_irq(irq, &fsi_interrupt, IRQF_DISABLED, "fsi", master); |
939 | if (ret) { | 966 | if (ret) { |
940 | dev_err(&pdev->dev, "irq request err\n"); | 967 | dev_err(&pdev->dev, "irq request err\n"); |
941 | goto exit_free_dma; | 968 | goto exit_iounmap; |
942 | } | 969 | } |
943 | 970 | ||
944 | ret = snd_soc_register_platform(&fsi_soc_platform); | 971 | ret = snd_soc_register_platform(&fsi_soc_platform); |
@@ -951,10 +978,9 @@ static int fsi_probe(struct platform_device *pdev) | |||
951 | 978 | ||
952 | exit_free_irq: | 979 | exit_free_irq: |
953 | free_irq(irq, master); | 980 | free_irq(irq, master); |
954 | exit_free_dma: | ||
955 | fsi_free_dma_chan(); | ||
956 | exit_iounmap: | 981 | exit_iounmap: |
957 | iounmap(master->base); | 982 | iounmap(master->base); |
983 | pm_runtime_disable(&pdev->dev); | ||
958 | exit_kfree: | 984 | exit_kfree: |
959 | kfree(master); | 985 | kfree(master); |
960 | master = NULL; | 986 | master = NULL; |
@@ -964,24 +990,49 @@ exit: | |||
964 | 990 | ||
965 | static int fsi_remove(struct platform_device *pdev) | 991 | static int fsi_remove(struct platform_device *pdev) |
966 | { | 992 | { |
993 | struct fsi_master *master; | ||
994 | |||
995 | master = fsi_get_master(fsi_soc_dai[0].private_data); | ||
996 | |||
967 | snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); | 997 | snd_soc_unregister_dais(fsi_soc_dai, ARRAY_SIZE(fsi_soc_dai)); |
968 | snd_soc_unregister_platform(&fsi_soc_platform); | 998 | snd_soc_unregister_platform(&fsi_soc_platform); |
969 | 999 | ||
970 | clk_put(master->clk); | 1000 | pm_runtime_disable(&pdev->dev); |
971 | |||
972 | fsi_free_dma_chan(); | ||
973 | 1001 | ||
974 | free_irq(master->irq, master); | 1002 | free_irq(master->irq, master); |
975 | 1003 | ||
976 | iounmap(master->base); | 1004 | iounmap(master->base); |
977 | kfree(master); | 1005 | kfree(master); |
978 | master = NULL; | 1006 | |
1007 | fsi_soc_dai[0].dev = NULL; | ||
1008 | fsi_soc_dai[0].private_data = NULL; | ||
1009 | fsi_soc_dai[1].dev = NULL; | ||
1010 | fsi_soc_dai[1].private_data = NULL; | ||
1011 | |||
1012 | return 0; | ||
1013 | } | ||
1014 | |||
1015 | static int fsi_runtime_nop(struct device *dev) | ||
1016 | { | ||
1017 | /* Runtime PM callback shared between ->runtime_suspend() | ||
1018 | * and ->runtime_resume(). Simply returns success. | ||
1019 | * | ||
1020 | * This driver re-initializes all registers after | ||
1021 | * pm_runtime_get_sync() anyway so there is no need | ||
1022 | * to save and restore registers here. | ||
1023 | */ | ||
979 | return 0; | 1024 | return 0; |
980 | } | 1025 | } |
981 | 1026 | ||
1027 | static struct dev_pm_ops fsi_pm_ops = { | ||
1028 | .runtime_suspend = fsi_runtime_nop, | ||
1029 | .runtime_resume = fsi_runtime_nop, | ||
1030 | }; | ||
1031 | |||
982 | static struct platform_driver fsi_driver = { | 1032 | static struct platform_driver fsi_driver = { |
983 | .driver = { | 1033 | .driver = { |
984 | .name = "sh_fsi", | 1034 | .name = "sh_fsi", |
1035 | .pm = &fsi_pm_ops, | ||
985 | }, | 1036 | }, |
986 | .probe = fsi_probe, | 1037 | .probe = fsi_probe, |
987 | .remove = fsi_remove, | 1038 | .remove = fsi_remove, |
diff --git a/sound/soc/sh/migor.c b/sound/soc/sh/migor.c new file mode 100644 index 000000000000..b823a5c9b9bc --- /dev/null +++ b/sound/soc/sh/migor.c | |||
@@ -0,0 +1,218 @@ | |||
1 | /* | ||
2 | * ALSA SoC driver for Migo-R | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/device.h> | ||
12 | #include <linux/firmware.h> | ||
13 | #include <linux/module.h> | ||
14 | |||
15 | #include <asm/clock.h> | ||
16 | |||
17 | #include <cpu/sh7722.h> | ||
18 | |||
19 | #include <sound/core.h> | ||
20 | #include <sound/pcm.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-dapm.h> | ||
23 | |||
24 | #include "../codecs/wm8978.h" | ||
25 | #include "siu.h" | ||
26 | |||
27 | /* Default 8000Hz sampling frequency */ | ||
28 | static unsigned long codec_freq = 8000 * 512; | ||
29 | |||
30 | static unsigned int use_count; | ||
31 | |||
32 | /* External clock, sourced from the codec at the SIUMCKB pin */ | ||
33 | static unsigned long siumckb_recalc(struct clk *clk) | ||
34 | { | ||
35 | return codec_freq; | ||
36 | } | ||
37 | |||
38 | static struct clk_ops siumckb_clk_ops = { | ||
39 | .recalc = siumckb_recalc, | ||
40 | }; | ||
41 | |||
42 | static struct clk siumckb_clk = { | ||
43 | .name = "siumckb_clk", | ||
44 | .id = -1, | ||
45 | .ops = &siumckb_clk_ops, | ||
46 | .rate = 0, /* initialised at run-time */ | ||
47 | }; | ||
48 | |||
49 | static int migor_hw_params(struct snd_pcm_substream *substream, | ||
50 | struct snd_pcm_hw_params *params) | ||
51 | { | ||
52 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
53 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
54 | int ret; | ||
55 | unsigned int rate = params_rate(params); | ||
56 | |||
57 | ret = snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 13000000, | ||
58 | SND_SOC_CLOCK_IN); | ||
59 | if (ret < 0) | ||
60 | return ret; | ||
61 | |||
62 | ret = snd_soc_dai_set_clkdiv(codec_dai, WM8978_OPCLKRATE, rate * 512); | ||
63 | if (ret < 0) | ||
64 | return ret; | ||
65 | |||
66 | ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_NB_IF | | ||
67 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS); | ||
68 | if (ret < 0) | ||
69 | return ret; | ||
70 | |||
71 | ret = snd_soc_dai_set_fmt(rtd->dai->cpu_dai, SND_SOC_DAIFMT_NB_IF | | ||
72 | SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS); | ||
73 | if (ret < 0) | ||
74 | return ret; | ||
75 | |||
76 | codec_freq = rate * 512; | ||
77 | /* | ||
78 | * This propagates the parent frequency change to children and | ||
79 | * recalculates the frequency table | ||
80 | */ | ||
81 | clk_set_rate(&siumckb_clk, codec_freq); | ||
82 | dev_dbg(codec_dai->dev, "%s: configure %luHz\n", __func__, codec_freq); | ||
83 | |||
84 | ret = snd_soc_dai_set_sysclk(rtd->dai->cpu_dai, SIU_CLKB_EXT, | ||
85 | codec_freq / 2, SND_SOC_CLOCK_IN); | ||
86 | |||
87 | if (!ret) | ||
88 | use_count++; | ||
89 | |||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | static int migor_hw_free(struct snd_pcm_substream *substream) | ||
94 | { | ||
95 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
96 | struct snd_soc_dai *codec_dai = rtd->dai->codec_dai; | ||
97 | |||
98 | if (use_count) { | ||
99 | use_count--; | ||
100 | |||
101 | if (!use_count) | ||
102 | snd_soc_dai_set_sysclk(codec_dai, WM8978_PLL, 0, | ||
103 | SND_SOC_CLOCK_IN); | ||
104 | } else { | ||
105 | dev_dbg(codec_dai->dev, "Unbalanced hw_free!\n"); | ||
106 | } | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static struct snd_soc_ops migor_dai_ops = { | ||
112 | .hw_params = migor_hw_params, | ||
113 | .hw_free = migor_hw_free, | ||
114 | }; | ||
115 | |||
116 | static const struct snd_soc_dapm_widget migor_dapm_widgets[] = { | ||
117 | SND_SOC_DAPM_HP("Headphone", NULL), | ||
118 | SND_SOC_DAPM_MIC("Onboard Microphone", NULL), | ||
119 | SND_SOC_DAPM_MIC("External Microphone", NULL), | ||
120 | }; | ||
121 | |||
122 | static const struct snd_soc_dapm_route audio_map[] = { | ||
123 | /* Headphone output connected to LHP/RHP, enable OUT4 for VMID */ | ||
124 | { "Headphone", NULL, "OUT4 VMID" }, | ||
125 | { "OUT4 VMID", NULL, "LHP" }, | ||
126 | { "OUT4 VMID", NULL, "RHP" }, | ||
127 | |||
128 | /* On-board microphone */ | ||
129 | { "RMICN", NULL, "Mic Bias" }, | ||
130 | { "RMICP", NULL, "Mic Bias" }, | ||
131 | { "Mic Bias", NULL, "Onboard Microphone" }, | ||
132 | |||
133 | /* External microphone */ | ||
134 | { "LMICN", NULL, "Mic Bias" }, | ||
135 | { "LMICP", NULL, "Mic Bias" }, | ||
136 | { "Mic Bias", NULL, "External Microphone" }, | ||
137 | }; | ||
138 | |||
139 | static int migor_dai_init(struct snd_soc_codec *codec) | ||
140 | { | ||
141 | snd_soc_dapm_new_controls(codec, migor_dapm_widgets, | ||
142 | ARRAY_SIZE(migor_dapm_widgets)); | ||
143 | |||
144 | snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* migor digital audio interface glue - connects codec <--> CPU */ | ||
150 | static struct snd_soc_dai_link migor_dai = { | ||
151 | .name = "wm8978", | ||
152 | .stream_name = "WM8978", | ||
153 | .cpu_dai = &siu_i2s_dai, | ||
154 | .codec_dai = &wm8978_dai, | ||
155 | .ops = &migor_dai_ops, | ||
156 | .init = migor_dai_init, | ||
157 | }; | ||
158 | |||
159 | /* migor audio machine driver */ | ||
160 | static struct snd_soc_card snd_soc_migor = { | ||
161 | .name = "Migo-R", | ||
162 | .platform = &siu_platform, | ||
163 | .dai_link = &migor_dai, | ||
164 | .num_links = 1, | ||
165 | }; | ||
166 | |||
167 | /* migor audio subsystem */ | ||
168 | static struct snd_soc_device migor_snd_devdata = { | ||
169 | .card = &snd_soc_migor, | ||
170 | .codec_dev = &soc_codec_dev_wm8978, | ||
171 | }; | ||
172 | |||
173 | static struct platform_device *migor_snd_device; | ||
174 | |||
175 | static int __init migor_init(void) | ||
176 | { | ||
177 | int ret; | ||
178 | |||
179 | ret = clk_register(&siumckb_clk); | ||
180 | if (ret < 0) | ||
181 | return ret; | ||
182 | |||
183 | /* Port number used on this machine: port B */ | ||
184 | migor_snd_device = platform_device_alloc("soc-audio", 1); | ||
185 | if (!migor_snd_device) { | ||
186 | ret = -ENOMEM; | ||
187 | goto epdevalloc; | ||
188 | } | ||
189 | |||
190 | platform_set_drvdata(migor_snd_device, &migor_snd_devdata); | ||
191 | |||
192 | migor_snd_devdata.dev = &migor_snd_device->dev; | ||
193 | |||
194 | ret = platform_device_add(migor_snd_device); | ||
195 | if (ret) | ||
196 | goto epdevadd; | ||
197 | |||
198 | return 0; | ||
199 | |||
200 | epdevadd: | ||
201 | platform_device_put(migor_snd_device); | ||
202 | epdevalloc: | ||
203 | clk_unregister(&siumckb_clk); | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | static void __exit migor_exit(void) | ||
208 | { | ||
209 | clk_unregister(&siumckb_clk); | ||
210 | platform_device_unregister(migor_snd_device); | ||
211 | } | ||
212 | |||
213 | module_init(migor_init); | ||
214 | module_exit(migor_exit); | ||
215 | |||
216 | MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); | ||
217 | MODULE_DESCRIPTION("ALSA SoC Migor"); | ||
218 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h new file mode 100644 index 000000000000..c0bfab8fed3d --- /dev/null +++ b/sound/soc/sh/siu.h | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * siu.h - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
5 | * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #ifndef SIU_H | ||
23 | #define SIU_H | ||
24 | |||
25 | /* Common kernel and user-space firmware-building defines and types */ | ||
26 | |||
27 | #define YRAM0_SIZE (0x0040 / 4) /* 16 */ | ||
28 | #define YRAM1_SIZE (0x0080 / 4) /* 32 */ | ||
29 | #define YRAM2_SIZE (0x0040 / 4) /* 16 */ | ||
30 | #define YRAM3_SIZE (0x0080 / 4) /* 32 */ | ||
31 | #define YRAM4_SIZE (0x0080 / 4) /* 32 */ | ||
32 | #define YRAM_DEF_SIZE (YRAM0_SIZE + YRAM1_SIZE + YRAM2_SIZE + \ | ||
33 | YRAM3_SIZE + YRAM4_SIZE) | ||
34 | #define YRAM_FIR_SIZE (0x0400 / 4) /* 256 */ | ||
35 | #define YRAM_IIR_SIZE (0x0200 / 4) /* 128 */ | ||
36 | |||
37 | #define XRAM0_SIZE (0x0400 / 4) /* 256 */ | ||
38 | #define XRAM1_SIZE (0x0200 / 4) /* 128 */ | ||
39 | #define XRAM2_SIZE (0x0200 / 4) /* 128 */ | ||
40 | |||
41 | /* PRAM program array size */ | ||
42 | #define PRAM0_SIZE (0x0100 / 4) /* 64 */ | ||
43 | #define PRAM1_SIZE ((0x2000 - 0x0100) / 4) /* 1984 */ | ||
44 | |||
45 | #include <linux/types.h> | ||
46 | |||
47 | struct siu_spb_param { | ||
48 | __u32 ab1a; /* input FIFO address */ | ||
49 | __u32 ab0a; /* output FIFO address */ | ||
50 | __u32 dir; /* 0=the ather except CPUOUTPUT, 1=CPUINPUT */ | ||
51 | __u32 event; /* SPB program starting conditions */ | ||
52 | __u32 stfifo; /* STFIFO register setting value */ | ||
53 | __u32 trdat; /* TRDAT register setting value */ | ||
54 | }; | ||
55 | |||
56 | struct siu_firmware { | ||
57 | __u32 yram_fir_coeff[YRAM_FIR_SIZE]; | ||
58 | __u32 pram0[PRAM0_SIZE]; | ||
59 | __u32 pram1[PRAM1_SIZE]; | ||
60 | __u32 yram0[YRAM0_SIZE]; | ||
61 | __u32 yram1[YRAM1_SIZE]; | ||
62 | __u32 yram2[YRAM2_SIZE]; | ||
63 | __u32 yram3[YRAM3_SIZE]; | ||
64 | __u32 yram4[YRAM4_SIZE]; | ||
65 | __u32 spbpar_num; | ||
66 | struct siu_spb_param spbpar[32]; | ||
67 | }; | ||
68 | |||
69 | #ifdef __KERNEL__ | ||
70 | |||
71 | #include <linux/dmaengine.h> | ||
72 | #include <linux/interrupt.h> | ||
73 | #include <linux/io.h> | ||
74 | |||
75 | #include <asm/dmaengine.h> | ||
76 | |||
77 | #include <sound/core.h> | ||
78 | #include <sound/pcm.h> | ||
79 | #include <sound/soc-dai.h> | ||
80 | |||
81 | #define SIU_PERIOD_BYTES_MAX 8192 /* DMA transfer/period size */ | ||
82 | #define SIU_PERIOD_BYTES_MIN 256 /* DMA transfer/period size */ | ||
83 | #define SIU_PERIODS_MAX 64 /* Max periods in buffer */ | ||
84 | #define SIU_PERIODS_MIN 4 /* Min periods in buffer */ | ||
85 | #define SIU_BUFFER_BYTES_MAX (SIU_PERIOD_BYTES_MAX * SIU_PERIODS_MAX) | ||
86 | |||
87 | /* SIU ports: only one can be used at a time */ | ||
88 | enum { | ||
89 | SIU_PORT_A, | ||
90 | SIU_PORT_B, | ||
91 | SIU_PORT_NUM, | ||
92 | }; | ||
93 | |||
94 | /* SIU clock configuration */ | ||
95 | enum { | ||
96 | SIU_CLKA_PLL, | ||
97 | SIU_CLKA_EXT, | ||
98 | SIU_CLKB_PLL, | ||
99 | SIU_CLKB_EXT | ||
100 | }; | ||
101 | |||
102 | struct siu_info { | ||
103 | int port_id; | ||
104 | u32 __iomem *pram; | ||
105 | u32 __iomem *xram; | ||
106 | u32 __iomem *yram; | ||
107 | u32 __iomem *reg; | ||
108 | struct siu_firmware fw; | ||
109 | }; | ||
110 | |||
111 | struct siu_stream { | ||
112 | struct tasklet_struct tasklet; | ||
113 | struct snd_pcm_substream *substream; | ||
114 | snd_pcm_format_t format; | ||
115 | size_t buf_bytes; | ||
116 | size_t period_bytes; | ||
117 | int cur_period; /* Period currently in dma */ | ||
118 | u32 volume; | ||
119 | snd_pcm_sframes_t xfer_cnt; /* Number of frames */ | ||
120 | u8 rw_flg; /* transfer status */ | ||
121 | /* DMA status */ | ||
122 | struct dma_chan *chan; /* DMA channel */ | ||
123 | struct dma_async_tx_descriptor *tx_desc; | ||
124 | dma_cookie_t cookie; | ||
125 | struct sh_dmae_slave param; | ||
126 | }; | ||
127 | |||
128 | struct siu_port { | ||
129 | unsigned long play_cap; /* Used to track full duplex */ | ||
130 | struct snd_pcm *pcm; | ||
131 | struct siu_stream playback; | ||
132 | struct siu_stream capture; | ||
133 | u32 stfifo; /* STFIFO value from firmware */ | ||
134 | u32 trdat; /* TRDAT value from firmware */ | ||
135 | }; | ||
136 | |||
137 | extern struct siu_port *siu_ports[SIU_PORT_NUM]; | ||
138 | |||
139 | static inline struct siu_port *siu_port_info(struct snd_pcm_substream *substream) | ||
140 | { | ||
141 | struct platform_device *pdev = | ||
142 | to_platform_device(substream->pcm->card->dev); | ||
143 | return siu_ports[pdev->id]; | ||
144 | } | ||
145 | |||
146 | /* Register access */ | ||
147 | static inline void siu_write32(u32 __iomem *addr, u32 val) | ||
148 | { | ||
149 | __raw_writel(val, addr); | ||
150 | } | ||
151 | |||
152 | static inline u32 siu_read32(u32 __iomem *addr) | ||
153 | { | ||
154 | return __raw_readl(addr); | ||
155 | } | ||
156 | |||
157 | /* SIU registers */ | ||
158 | #define SIU_IFCTL (0x000 / sizeof(u32)) | ||
159 | #define SIU_SRCTL (0x004 / sizeof(u32)) | ||
160 | #define SIU_SFORM (0x008 / sizeof(u32)) | ||
161 | #define SIU_CKCTL (0x00c / sizeof(u32)) | ||
162 | #define SIU_TRDAT (0x010 / sizeof(u32)) | ||
163 | #define SIU_STFIFO (0x014 / sizeof(u32)) | ||
164 | #define SIU_DPAK (0x01c / sizeof(u32)) | ||
165 | #define SIU_CKREV (0x020 / sizeof(u32)) | ||
166 | #define SIU_EVNTC (0x028 / sizeof(u32)) | ||
167 | #define SIU_SBCTL (0x040 / sizeof(u32)) | ||
168 | #define SIU_SBPSET (0x044 / sizeof(u32)) | ||
169 | #define SIU_SBFSTS (0x068 / sizeof(u32)) | ||
170 | #define SIU_SBDVCA (0x06c / sizeof(u32)) | ||
171 | #define SIU_SBDVCB (0x070 / sizeof(u32)) | ||
172 | #define SIU_SBACTIV (0x074 / sizeof(u32)) | ||
173 | #define SIU_DMAIA (0x090 / sizeof(u32)) | ||
174 | #define SIU_DMAIB (0x094 / sizeof(u32)) | ||
175 | #define SIU_DMAOA (0x098 / sizeof(u32)) | ||
176 | #define SIU_DMAOB (0x09c / sizeof(u32)) | ||
177 | #define SIU_DMAML (0x0a0 / sizeof(u32)) | ||
178 | #define SIU_SPSTS (0x0cc / sizeof(u32)) | ||
179 | #define SIU_SPCTL (0x0d0 / sizeof(u32)) | ||
180 | #define SIU_BRGASEL (0x100 / sizeof(u32)) | ||
181 | #define SIU_BRRA (0x104 / sizeof(u32)) | ||
182 | #define SIU_BRGBSEL (0x108 / sizeof(u32)) | ||
183 | #define SIU_BRRB (0x10c / sizeof(u32)) | ||
184 | |||
185 | extern struct snd_soc_platform siu_platform; | ||
186 | extern struct snd_soc_dai siu_i2s_dai; | ||
187 | |||
188 | int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card); | ||
189 | void siu_free_port(struct siu_port *port_info); | ||
190 | |||
191 | #endif | ||
192 | |||
193 | #endif /* SIU_H */ | ||
diff --git a/sound/soc/sh/siu_dai.c b/sound/soc/sh/siu_dai.c new file mode 100644 index 000000000000..d86ee1bfc03a --- /dev/null +++ b/sound/soc/sh/siu_dai.c | |||
@@ -0,0 +1,848 @@ | |||
1 | /* | ||
2 | * siu_dai.c - ALSA SoC driver for Renesas SH7343, SH7722 SIU peripheral. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
5 | * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/delay.h> | ||
23 | #include <linux/firmware.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | #include <linux/slab.h> | ||
26 | |||
27 | #include <asm/clock.h> | ||
28 | #include <asm/siu.h> | ||
29 | |||
30 | #include <sound/control.h> | ||
31 | #include <sound/soc-dai.h> | ||
32 | |||
33 | #include "siu.h" | ||
34 | |||
35 | /* Board specifics */ | ||
36 | #if defined(CONFIG_CPU_SUBTYPE_SH7722) | ||
37 | # define SIU_MAX_VOLUME 0x1000 | ||
38 | #else | ||
39 | # define SIU_MAX_VOLUME 0x7fff | ||
40 | #endif | ||
41 | |||
42 | #define PRAM_SIZE 0x2000 | ||
43 | #define XRAM_SIZE 0x800 | ||
44 | #define YRAM_SIZE 0x800 | ||
45 | |||
46 | #define XRAM_OFFSET 0x4000 | ||
47 | #define YRAM_OFFSET 0x6000 | ||
48 | #define REG_OFFSET 0xc000 | ||
49 | |||
50 | #define PLAYBACK_ENABLED 1 | ||
51 | #define CAPTURE_ENABLED 2 | ||
52 | |||
53 | #define VOLUME_CAPTURE 0 | ||
54 | #define VOLUME_PLAYBACK 1 | ||
55 | #define DFLT_VOLUME_LEVEL 0x08000800 | ||
56 | |||
57 | /* | ||
58 | * SPDIF is only available on port A and on some SIU implementations it is only | ||
59 | * available for input. Due to the lack of hardware to test it, SPDIF is left | ||
60 | * disabled in this driver version | ||
61 | */ | ||
62 | struct format_flag { | ||
63 | u32 i2s; | ||
64 | u32 pcm; | ||
65 | u32 spdif; | ||
66 | u32 mask; | ||
67 | }; | ||
68 | |||
69 | struct port_flag { | ||
70 | struct format_flag playback; | ||
71 | struct format_flag capture; | ||
72 | }; | ||
73 | |||
74 | static struct port_flag siu_flags[SIU_PORT_NUM] = { | ||
75 | [SIU_PORT_A] = { | ||
76 | .playback = { | ||
77 | .i2s = 0x50000000, | ||
78 | .pcm = 0x40000000, | ||
79 | .spdif = 0x80000000, /* not on all SIU versions */ | ||
80 | .mask = 0xd0000000, | ||
81 | }, | ||
82 | .capture = { | ||
83 | .i2s = 0x05000000, | ||
84 | .pcm = 0x04000000, | ||
85 | .spdif = 0x08000000, | ||
86 | .mask = 0x0d000000, | ||
87 | }, | ||
88 | }, | ||
89 | [SIU_PORT_B] = { | ||
90 | .playback = { | ||
91 | .i2s = 0x00500000, | ||
92 | .pcm = 0x00400000, | ||
93 | .spdif = 0, /* impossible - turn off */ | ||
94 | .mask = 0x00500000, | ||
95 | }, | ||
96 | .capture = { | ||
97 | .i2s = 0x00050000, | ||
98 | .pcm = 0x00040000, | ||
99 | .spdif = 0, /* impossible - turn off */ | ||
100 | .mask = 0x00050000, | ||
101 | }, | ||
102 | }, | ||
103 | }; | ||
104 | |||
105 | static void siu_dai_start(struct siu_port *port_info) | ||
106 | { | ||
107 | struct siu_info *info = siu_i2s_dai.private_data; | ||
108 | u32 __iomem *base = info->reg; | ||
109 | |||
110 | dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); | ||
111 | |||
112 | /* Turn on SIU clock */ | ||
113 | pm_runtime_get_sync(siu_i2s_dai.dev); | ||
114 | |||
115 | /* Issue software reset to siu */ | ||
116 | siu_write32(base + SIU_SRCTL, 0); | ||
117 | |||
118 | /* Wait for the reset to take effect */ | ||
119 | udelay(1); | ||
120 | |||
121 | port_info->stfifo = 0; | ||
122 | port_info->trdat = 0; | ||
123 | |||
124 | /* portA, portB, SIU operate */ | ||
125 | siu_write32(base + SIU_SRCTL, 0x301); | ||
126 | |||
127 | /* portA=256fs, portB=256fs */ | ||
128 | siu_write32(base + SIU_CKCTL, 0x40400000); | ||
129 | |||
130 | /* portA's BRG does not divide SIUCKA */ | ||
131 | siu_write32(base + SIU_BRGASEL, 0); | ||
132 | siu_write32(base + SIU_BRRA, 0); | ||
133 | |||
134 | /* portB's BRG divides SIUCKB by half */ | ||
135 | siu_write32(base + SIU_BRGBSEL, 1); | ||
136 | siu_write32(base + SIU_BRRB, 0); | ||
137 | |||
138 | siu_write32(base + SIU_IFCTL, 0x44440000); | ||
139 | |||
140 | /* portA: 32 bit/fs, master; portB: 32 bit/fs, master */ | ||
141 | siu_write32(base + SIU_SFORM, 0x0c0c0000); | ||
142 | |||
143 | /* | ||
144 | * Volume levels: looks like the DSP firmware implements volume controls | ||
145 | * differently from what's described in the datasheet | ||
146 | */ | ||
147 | siu_write32(base + SIU_SBDVCA, port_info->playback.volume); | ||
148 | siu_write32(base + SIU_SBDVCB, port_info->capture.volume); | ||
149 | } | ||
150 | |||
151 | static void siu_dai_stop(void) | ||
152 | { | ||
153 | struct siu_info *info = siu_i2s_dai.private_data; | ||
154 | u32 __iomem *base = info->reg; | ||
155 | |||
156 | /* SIU software reset */ | ||
157 | siu_write32(base + SIU_SRCTL, 0); | ||
158 | |||
159 | /* Turn off SIU clock */ | ||
160 | pm_runtime_put_sync(siu_i2s_dai.dev); | ||
161 | } | ||
162 | |||
163 | static void siu_dai_spbAselect(struct siu_port *port_info) | ||
164 | { | ||
165 | struct siu_info *info = siu_i2s_dai.private_data; | ||
166 | struct siu_firmware *fw = &info->fw; | ||
167 | u32 *ydef = fw->yram0; | ||
168 | u32 idx; | ||
169 | |||
170 | /* path A use */ | ||
171 | if (!info->port_id) | ||
172 | idx = 1; /* portA */ | ||
173 | else | ||
174 | idx = 2; /* portB */ | ||
175 | |||
176 | ydef[0] = (fw->spbpar[idx].ab1a << 16) | | ||
177 | (fw->spbpar[idx].ab0a << 8) | | ||
178 | (fw->spbpar[idx].dir << 7) | 3; | ||
179 | ydef[1] = fw->yram0[1]; /* 0x03000300 */ | ||
180 | ydef[2] = (16 / 2) << 24; | ||
181 | ydef[3] = fw->yram0[3]; /* 0 */ | ||
182 | ydef[4] = fw->yram0[4]; /* 0 */ | ||
183 | ydef[7] = fw->spbpar[idx].event; | ||
184 | port_info->stfifo |= fw->spbpar[idx].stfifo; | ||
185 | port_info->trdat |= fw->spbpar[idx].trdat; | ||
186 | } | ||
187 | |||
188 | static void siu_dai_spbBselect(struct siu_port *port_info) | ||
189 | { | ||
190 | struct siu_info *info = siu_i2s_dai.private_data; | ||
191 | struct siu_firmware *fw = &info->fw; | ||
192 | u32 *ydef = fw->yram0; | ||
193 | u32 idx; | ||
194 | |||
195 | /* path B use */ | ||
196 | if (!info->port_id) | ||
197 | idx = 7; /* portA */ | ||
198 | else | ||
199 | idx = 8; /* portB */ | ||
200 | |||
201 | ydef[5] = (fw->spbpar[idx].ab1a << 16) | | ||
202 | (fw->spbpar[idx].ab0a << 8) | 1; | ||
203 | ydef[6] = fw->spbpar[idx].event; | ||
204 | port_info->stfifo |= fw->spbpar[idx].stfifo; | ||
205 | port_info->trdat |= fw->spbpar[idx].trdat; | ||
206 | } | ||
207 | |||
208 | static void siu_dai_open(struct siu_stream *siu_stream) | ||
209 | { | ||
210 | struct siu_info *info = siu_i2s_dai.private_data; | ||
211 | u32 __iomem *base = info->reg; | ||
212 | u32 srctl, ifctl; | ||
213 | |||
214 | srctl = siu_read32(base + SIU_SRCTL); | ||
215 | ifctl = siu_read32(base + SIU_IFCTL); | ||
216 | |||
217 | switch (info->port_id) { | ||
218 | case SIU_PORT_A: | ||
219 | /* portA operates */ | ||
220 | srctl |= 0x200; | ||
221 | ifctl &= ~0xc2; | ||
222 | break; | ||
223 | case SIU_PORT_B: | ||
224 | /* portB operates */ | ||
225 | srctl |= 0x100; | ||
226 | ifctl &= ~0x31; | ||
227 | break; | ||
228 | } | ||
229 | |||
230 | siu_write32(base + SIU_SRCTL, srctl); | ||
231 | /* Unmute and configure portA */ | ||
232 | siu_write32(base + SIU_IFCTL, ifctl); | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * At the moment only fixed Left-upper, Left-lower, Right-upper, Right-lower | ||
237 | * packing is supported | ||
238 | */ | ||
239 | static void siu_dai_pcmdatapack(struct siu_stream *siu_stream) | ||
240 | { | ||
241 | struct siu_info *info = siu_i2s_dai.private_data; | ||
242 | u32 __iomem *base = info->reg; | ||
243 | u32 dpak; | ||
244 | |||
245 | dpak = siu_read32(base + SIU_DPAK); | ||
246 | |||
247 | switch (info->port_id) { | ||
248 | case SIU_PORT_A: | ||
249 | dpak &= ~0xc0000000; | ||
250 | break; | ||
251 | case SIU_PORT_B: | ||
252 | dpak &= ~0x00c00000; | ||
253 | break; | ||
254 | } | ||
255 | |||
256 | siu_write32(base + SIU_DPAK, dpak); | ||
257 | } | ||
258 | |||
259 | static int siu_dai_spbstart(struct siu_port *port_info) | ||
260 | { | ||
261 | struct siu_info *info = siu_i2s_dai.private_data; | ||
262 | u32 __iomem *base = info->reg; | ||
263 | struct siu_firmware *fw = &info->fw; | ||
264 | u32 *ydef = fw->yram0; | ||
265 | int cnt; | ||
266 | u32 __iomem *add; | ||
267 | u32 *ptr; | ||
268 | |||
269 | /* Load SPB Program in PRAM */ | ||
270 | ptr = fw->pram0; | ||
271 | add = info->pram; | ||
272 | for (cnt = 0; cnt < PRAM0_SIZE; cnt++, add++, ptr++) | ||
273 | siu_write32(add, *ptr); | ||
274 | |||
275 | ptr = fw->pram1; | ||
276 | add = info->pram + (0x0100 / sizeof(u32)); | ||
277 | for (cnt = 0; cnt < PRAM1_SIZE; cnt++, add++, ptr++) | ||
278 | siu_write32(add, *ptr); | ||
279 | |||
280 | /* XRAM initialization */ | ||
281 | add = info->xram; | ||
282 | for (cnt = 0; cnt < XRAM0_SIZE + XRAM1_SIZE + XRAM2_SIZE; cnt++, add++) | ||
283 | siu_write32(add, 0); | ||
284 | |||
285 | /* YRAM variable area initialization */ | ||
286 | add = info->yram; | ||
287 | for (cnt = 0; cnt < YRAM_DEF_SIZE; cnt++, add++) | ||
288 | siu_write32(add, ydef[cnt]); | ||
289 | |||
290 | /* YRAM FIR coefficient area initialization */ | ||
291 | add = info->yram + (0x0200 / sizeof(u32)); | ||
292 | for (cnt = 0; cnt < YRAM_FIR_SIZE; cnt++, add++) | ||
293 | siu_write32(add, fw->yram_fir_coeff[cnt]); | ||
294 | |||
295 | /* YRAM IIR coefficient area initialization */ | ||
296 | add = info->yram + (0x0600 / sizeof(u32)); | ||
297 | for (cnt = 0; cnt < YRAM_IIR_SIZE; cnt++, add++) | ||
298 | siu_write32(add, 0); | ||
299 | |||
300 | siu_write32(base + SIU_TRDAT, port_info->trdat); | ||
301 | port_info->trdat = 0x0; | ||
302 | |||
303 | |||
304 | /* SPB start condition: software */ | ||
305 | siu_write32(base + SIU_SBACTIV, 0); | ||
306 | /* Start SPB */ | ||
307 | siu_write32(base + SIU_SBCTL, 0xc0000000); | ||
308 | /* Wait for program to halt */ | ||
309 | cnt = 0x10000; | ||
310 | while (--cnt && siu_read32(base + SIU_SBCTL) != 0x80000000) | ||
311 | cpu_relax(); | ||
312 | |||
313 | if (!cnt) | ||
314 | return -EBUSY; | ||
315 | |||
316 | /* SPB program start address setting */ | ||
317 | siu_write32(base + SIU_SBPSET, 0x00400000); | ||
318 | /* SPB hardware start(FIFOCTL source) */ | ||
319 | siu_write32(base + SIU_SBACTIV, 0xc0000000); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static void siu_dai_spbstop(struct siu_port *port_info) | ||
325 | { | ||
326 | struct siu_info *info = siu_i2s_dai.private_data; | ||
327 | u32 __iomem *base = info->reg; | ||
328 | |||
329 | siu_write32(base + SIU_SBACTIV, 0); | ||
330 | /* SPB stop */ | ||
331 | siu_write32(base + SIU_SBCTL, 0); | ||
332 | |||
333 | port_info->stfifo = 0; | ||
334 | } | ||
335 | |||
336 | /* API functions */ | ||
337 | |||
338 | /* Playback and capture hardware properties are identical */ | ||
339 | static struct snd_pcm_hardware siu_dai_pcm_hw = { | ||
340 | .info = SNDRV_PCM_INFO_INTERLEAVED, | ||
341 | .formats = SNDRV_PCM_FMTBIT_S16, | ||
342 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
343 | .rate_min = 8000, | ||
344 | .rate_max = 48000, | ||
345 | .channels_min = 2, | ||
346 | .channels_max = 2, | ||
347 | .buffer_bytes_max = SIU_BUFFER_BYTES_MAX, | ||
348 | .period_bytes_min = SIU_PERIOD_BYTES_MIN, | ||
349 | .period_bytes_max = SIU_PERIOD_BYTES_MAX, | ||
350 | .periods_min = SIU_PERIODS_MIN, | ||
351 | .periods_max = SIU_PERIODS_MAX, | ||
352 | }; | ||
353 | |||
354 | static int siu_dai_info_volume(struct snd_kcontrol *kctrl, | ||
355 | struct snd_ctl_elem_info *uinfo) | ||
356 | { | ||
357 | struct siu_port *port_info = snd_kcontrol_chip(kctrl); | ||
358 | |||
359 | dev_dbg(port_info->pcm->card->dev, "%s\n", __func__); | ||
360 | |||
361 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
362 | uinfo->count = 2; | ||
363 | uinfo->value.integer.min = 0; | ||
364 | uinfo->value.integer.max = SIU_MAX_VOLUME; | ||
365 | |||
366 | return 0; | ||
367 | } | ||
368 | |||
369 | static int siu_dai_get_volume(struct snd_kcontrol *kctrl, | ||
370 | struct snd_ctl_elem_value *ucontrol) | ||
371 | { | ||
372 | struct siu_port *port_info = snd_kcontrol_chip(kctrl); | ||
373 | struct device *dev = port_info->pcm->card->dev; | ||
374 | u32 vol; | ||
375 | |||
376 | dev_dbg(dev, "%s\n", __func__); | ||
377 | |||
378 | switch (kctrl->private_value) { | ||
379 | case VOLUME_PLAYBACK: | ||
380 | /* Playback is always on port 0 */ | ||
381 | vol = port_info->playback.volume; | ||
382 | ucontrol->value.integer.value[0] = vol & 0xffff; | ||
383 | ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; | ||
384 | break; | ||
385 | case VOLUME_CAPTURE: | ||
386 | /* Capture is always on port 1 */ | ||
387 | vol = port_info->capture.volume; | ||
388 | ucontrol->value.integer.value[0] = vol & 0xffff; | ||
389 | ucontrol->value.integer.value[1] = vol >> 16 & 0xffff; | ||
390 | break; | ||
391 | default: | ||
392 | dev_err(dev, "%s() invalid private_value=%ld\n", | ||
393 | __func__, kctrl->private_value); | ||
394 | return -EINVAL; | ||
395 | } | ||
396 | |||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | static int siu_dai_put_volume(struct snd_kcontrol *kctrl, | ||
401 | struct snd_ctl_elem_value *ucontrol) | ||
402 | { | ||
403 | struct siu_port *port_info = snd_kcontrol_chip(kctrl); | ||
404 | struct device *dev = port_info->pcm->card->dev; | ||
405 | struct siu_info *info = siu_i2s_dai.private_data; | ||
406 | u32 __iomem *base = info->reg; | ||
407 | u32 new_vol; | ||
408 | u32 cur_vol; | ||
409 | |||
410 | dev_dbg(dev, "%s\n", __func__); | ||
411 | |||
412 | if (ucontrol->value.integer.value[0] < 0 || | ||
413 | ucontrol->value.integer.value[0] > SIU_MAX_VOLUME || | ||
414 | ucontrol->value.integer.value[1] < 0 || | ||
415 | ucontrol->value.integer.value[1] > SIU_MAX_VOLUME) | ||
416 | return -EINVAL; | ||
417 | |||
418 | new_vol = ucontrol->value.integer.value[0] | | ||
419 | ucontrol->value.integer.value[1] << 16; | ||
420 | |||
421 | /* See comment above - DSP firmware implementation */ | ||
422 | switch (kctrl->private_value) { | ||
423 | case VOLUME_PLAYBACK: | ||
424 | /* Playback is always on port 0 */ | ||
425 | cur_vol = port_info->playback.volume; | ||
426 | siu_write32(base + SIU_SBDVCA, new_vol); | ||
427 | port_info->playback.volume = new_vol; | ||
428 | break; | ||
429 | case VOLUME_CAPTURE: | ||
430 | /* Capture is always on port 1 */ | ||
431 | cur_vol = port_info->capture.volume; | ||
432 | siu_write32(base + SIU_SBDVCB, new_vol); | ||
433 | port_info->capture.volume = new_vol; | ||
434 | break; | ||
435 | default: | ||
436 | dev_err(dev, "%s() invalid private_value=%ld\n", | ||
437 | __func__, kctrl->private_value); | ||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | if (cur_vol != new_vol) | ||
442 | return 1; | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static struct snd_kcontrol_new playback_controls = { | ||
448 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
449 | .name = "PCM Playback Volume", | ||
450 | .index = 0, | ||
451 | .info = siu_dai_info_volume, | ||
452 | .get = siu_dai_get_volume, | ||
453 | .put = siu_dai_put_volume, | ||
454 | .private_value = VOLUME_PLAYBACK, | ||
455 | }; | ||
456 | |||
457 | static struct snd_kcontrol_new capture_controls = { | ||
458 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
459 | .name = "PCM Capture Volume", | ||
460 | .index = 0, | ||
461 | .info = siu_dai_info_volume, | ||
462 | .get = siu_dai_get_volume, | ||
463 | .put = siu_dai_put_volume, | ||
464 | .private_value = VOLUME_CAPTURE, | ||
465 | }; | ||
466 | |||
467 | int siu_init_port(int port, struct siu_port **port_info, struct snd_card *card) | ||
468 | { | ||
469 | struct device *dev = card->dev; | ||
470 | struct snd_kcontrol *kctrl; | ||
471 | int ret; | ||
472 | |||
473 | *port_info = kzalloc(sizeof(**port_info), GFP_KERNEL); | ||
474 | if (!*port_info) | ||
475 | return -ENOMEM; | ||
476 | |||
477 | dev_dbg(dev, "%s: port #%d@%p\n", __func__, port, *port_info); | ||
478 | |||
479 | (*port_info)->playback.volume = DFLT_VOLUME_LEVEL; | ||
480 | (*port_info)->capture.volume = DFLT_VOLUME_LEVEL; | ||
481 | |||
482 | /* | ||
483 | * Add mixer support. The SPB is used to change the volume. Both | ||
484 | * ports use the same SPB. Therefore, we only register one | ||
485 | * control instance since it will be used by both channels. | ||
486 | * In error case we continue without controls. | ||
487 | */ | ||
488 | kctrl = snd_ctl_new1(&playback_controls, *port_info); | ||
489 | ret = snd_ctl_add(card, kctrl); | ||
490 | if (ret < 0) | ||
491 | dev_err(dev, | ||
492 | "failed to add playback controls %p port=%d err=%d\n", | ||
493 | kctrl, port, ret); | ||
494 | |||
495 | kctrl = snd_ctl_new1(&capture_controls, *port_info); | ||
496 | ret = snd_ctl_add(card, kctrl); | ||
497 | if (ret < 0) | ||
498 | dev_err(dev, | ||
499 | "failed to add capture controls %p port=%d err=%d\n", | ||
500 | kctrl, port, ret); | ||
501 | |||
502 | return 0; | ||
503 | } | ||
504 | |||
505 | void siu_free_port(struct siu_port *port_info) | ||
506 | { | ||
507 | kfree(port_info); | ||
508 | } | ||
509 | |||
510 | static int siu_dai_startup(struct snd_pcm_substream *substream, | ||
511 | struct snd_soc_dai *dai) | ||
512 | { | ||
513 | struct siu_info *info = siu_i2s_dai.private_data; | ||
514 | struct snd_pcm_runtime *rt = substream->runtime; | ||
515 | struct siu_port *port_info = siu_port_info(substream); | ||
516 | int ret; | ||
517 | |||
518 | dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, | ||
519 | info->port_id, port_info); | ||
520 | |||
521 | snd_soc_set_runtime_hwparams(substream, &siu_dai_pcm_hw); | ||
522 | |||
523 | ret = snd_pcm_hw_constraint_integer(rt, SNDRV_PCM_HW_PARAM_PERIODS); | ||
524 | if (unlikely(ret < 0)) | ||
525 | return ret; | ||
526 | |||
527 | siu_dai_start(port_info); | ||
528 | |||
529 | return 0; | ||
530 | } | ||
531 | |||
532 | static void siu_dai_shutdown(struct snd_pcm_substream *substream, | ||
533 | struct snd_soc_dai *dai) | ||
534 | { | ||
535 | struct siu_info *info = siu_i2s_dai.private_data; | ||
536 | struct siu_port *port_info = siu_port_info(substream); | ||
537 | |||
538 | dev_dbg(substream->pcm->card->dev, "%s: port=%d@%p\n", __func__, | ||
539 | info->port_id, port_info); | ||
540 | |||
541 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
542 | port_info->play_cap &= ~PLAYBACK_ENABLED; | ||
543 | else | ||
544 | port_info->play_cap &= ~CAPTURE_ENABLED; | ||
545 | |||
546 | /* Stop the siu if the other stream is not using it */ | ||
547 | if (!port_info->play_cap) { | ||
548 | /* during stmread or stmwrite ? */ | ||
549 | BUG_ON(port_info->playback.rw_flg || port_info->capture.rw_flg); | ||
550 | siu_dai_spbstop(port_info); | ||
551 | siu_dai_stop(); | ||
552 | } | ||
553 | } | ||
554 | |||
555 | /* PCM part of siu_dai_playback_prepare() / siu_dai_capture_prepare() */ | ||
556 | static int siu_dai_prepare(struct snd_pcm_substream *substream, | ||
557 | struct snd_soc_dai *dai) | ||
558 | { | ||
559 | struct siu_info *info = siu_i2s_dai.private_data; | ||
560 | struct snd_pcm_runtime *rt = substream->runtime; | ||
561 | struct siu_port *port_info = siu_port_info(substream); | ||
562 | struct siu_stream *siu_stream; | ||
563 | int self, ret; | ||
564 | |||
565 | dev_dbg(substream->pcm->card->dev, | ||
566 | "%s: port %d, active streams %lx, %d channels\n", | ||
567 | __func__, info->port_id, port_info->play_cap, rt->channels); | ||
568 | |||
569 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
570 | self = PLAYBACK_ENABLED; | ||
571 | siu_stream = &port_info->playback; | ||
572 | } else { | ||
573 | self = CAPTURE_ENABLED; | ||
574 | siu_stream = &port_info->capture; | ||
575 | } | ||
576 | |||
577 | /* Set up the siu if not already done */ | ||
578 | if (!port_info->play_cap) { | ||
579 | siu_stream->rw_flg = 0; /* stream-data transfer flag */ | ||
580 | |||
581 | siu_dai_spbAselect(port_info); | ||
582 | siu_dai_spbBselect(port_info); | ||
583 | |||
584 | siu_dai_open(siu_stream); | ||
585 | |||
586 | siu_dai_pcmdatapack(siu_stream); | ||
587 | |||
588 | ret = siu_dai_spbstart(port_info); | ||
589 | if (ret < 0) | ||
590 | goto fail; | ||
591 | } | ||
592 | |||
593 | port_info->play_cap |= self; | ||
594 | |||
595 | fail: | ||
596 | return ret; | ||
597 | } | ||
598 | |||
599 | /* | ||
600 | * SIU can set bus format to I2S / PCM / SPDIF independently for playback and | ||
601 | * capture, however, the current API sets the bus format globally for a DAI. | ||
602 | */ | ||
603 | static int siu_dai_set_fmt(struct snd_soc_dai *dai, | ||
604 | unsigned int fmt) | ||
605 | { | ||
606 | struct siu_info *info = siu_i2s_dai.private_data; | ||
607 | u32 __iomem *base = info->reg; | ||
608 | u32 ifctl; | ||
609 | |||
610 | dev_dbg(dai->dev, "%s: fmt 0x%x on port %d\n", | ||
611 | __func__, fmt, info->port_id); | ||
612 | |||
613 | if (info->port_id < 0) | ||
614 | return -ENODEV; | ||
615 | |||
616 | /* Here select between I2S / PCM / SPDIF */ | ||
617 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
618 | case SND_SOC_DAIFMT_I2S: | ||
619 | ifctl = siu_flags[info->port_id].playback.i2s | | ||
620 | siu_flags[info->port_id].capture.i2s; | ||
621 | break; | ||
622 | case SND_SOC_DAIFMT_LEFT_J: | ||
623 | ifctl = siu_flags[info->port_id].playback.pcm | | ||
624 | siu_flags[info->port_id].capture.pcm; | ||
625 | break; | ||
626 | /* SPDIF disabled - see comment at the top */ | ||
627 | default: | ||
628 | return -EINVAL; | ||
629 | } | ||
630 | |||
631 | ifctl |= ~(siu_flags[info->port_id].playback.mask | | ||
632 | siu_flags[info->port_id].capture.mask) & | ||
633 | siu_read32(base + SIU_IFCTL); | ||
634 | siu_write32(base + SIU_IFCTL, ifctl); | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static int siu_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
640 | unsigned int freq, int dir) | ||
641 | { | ||
642 | struct clk *siu_clk, *parent_clk; | ||
643 | char *siu_name, *parent_name; | ||
644 | int ret; | ||
645 | |||
646 | if (dir != SND_SOC_CLOCK_IN) | ||
647 | return -EINVAL; | ||
648 | |||
649 | dev_dbg(dai->dev, "%s: using clock %d\n", __func__, clk_id); | ||
650 | |||
651 | switch (clk_id) { | ||
652 | case SIU_CLKA_PLL: | ||
653 | siu_name = "siua_clk"; | ||
654 | parent_name = "pll_clk"; | ||
655 | break; | ||
656 | case SIU_CLKA_EXT: | ||
657 | siu_name = "siua_clk"; | ||
658 | parent_name = "siumcka_clk"; | ||
659 | break; | ||
660 | case SIU_CLKB_PLL: | ||
661 | siu_name = "siub_clk"; | ||
662 | parent_name = "pll_clk"; | ||
663 | break; | ||
664 | case SIU_CLKB_EXT: | ||
665 | siu_name = "siub_clk"; | ||
666 | parent_name = "siumckb_clk"; | ||
667 | break; | ||
668 | default: | ||
669 | return -EINVAL; | ||
670 | } | ||
671 | |||
672 | siu_clk = clk_get(siu_i2s_dai.dev, siu_name); | ||
673 | if (IS_ERR(siu_clk)) | ||
674 | return PTR_ERR(siu_clk); | ||
675 | |||
676 | parent_clk = clk_get(siu_i2s_dai.dev, parent_name); | ||
677 | if (!IS_ERR(parent_clk)) { | ||
678 | ret = clk_set_parent(siu_clk, parent_clk); | ||
679 | if (!ret) | ||
680 | clk_set_rate(siu_clk, freq); | ||
681 | clk_put(parent_clk); | ||
682 | } | ||
683 | |||
684 | clk_put(siu_clk); | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static struct snd_soc_dai_ops siu_dai_ops = { | ||
690 | .startup = siu_dai_startup, | ||
691 | .shutdown = siu_dai_shutdown, | ||
692 | .prepare = siu_dai_prepare, | ||
693 | .set_sysclk = siu_dai_set_sysclk, | ||
694 | .set_fmt = siu_dai_set_fmt, | ||
695 | }; | ||
696 | |||
697 | struct snd_soc_dai siu_i2s_dai = { | ||
698 | .name = "sh-siu", | ||
699 | .id = 0, | ||
700 | .playback = { | ||
701 | .channels_min = 2, | ||
702 | .channels_max = 2, | ||
703 | .formats = SNDRV_PCM_FMTBIT_S16, | ||
704 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
705 | }, | ||
706 | .capture = { | ||
707 | .channels_min = 2, | ||
708 | .channels_max = 2, | ||
709 | .formats = SNDRV_PCM_FMTBIT_S16, | ||
710 | .rates = SNDRV_PCM_RATE_8000_48000, | ||
711 | }, | ||
712 | .ops = &siu_dai_ops, | ||
713 | }; | ||
714 | EXPORT_SYMBOL_GPL(siu_i2s_dai); | ||
715 | |||
716 | static int __devinit siu_probe(struct platform_device *pdev) | ||
717 | { | ||
718 | const struct firmware *fw_entry; | ||
719 | struct resource *res, *region; | ||
720 | struct siu_info *info; | ||
721 | int ret; | ||
722 | |||
723 | info = kmalloc(sizeof(*info), GFP_KERNEL); | ||
724 | if (!info) | ||
725 | return -ENOMEM; | ||
726 | |||
727 | ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev); | ||
728 | if (ret) | ||
729 | goto ereqfw; | ||
730 | |||
731 | /* | ||
732 | * Loaded firmware is "const" - read only, but we have to modify it in | ||
733 | * snd_siu_sh7343_spbAselect() and snd_siu_sh7343_spbBselect() | ||
734 | */ | ||
735 | memcpy(&info->fw, fw_entry->data, fw_entry->size); | ||
736 | |||
737 | release_firmware(fw_entry); | ||
738 | |||
739 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
740 | if (!res) { | ||
741 | ret = -ENODEV; | ||
742 | goto egetres; | ||
743 | } | ||
744 | |||
745 | region = request_mem_region(res->start, resource_size(res), | ||
746 | pdev->name); | ||
747 | if (!region) { | ||
748 | dev_err(&pdev->dev, "SIU region already claimed\n"); | ||
749 | ret = -EBUSY; | ||
750 | goto ereqmemreg; | ||
751 | } | ||
752 | |||
753 | ret = -ENOMEM; | ||
754 | info->pram = ioremap(res->start, PRAM_SIZE); | ||
755 | if (!info->pram) | ||
756 | goto emappram; | ||
757 | info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE); | ||
758 | if (!info->xram) | ||
759 | goto emapxram; | ||
760 | info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE); | ||
761 | if (!info->yram) | ||
762 | goto emapyram; | ||
763 | info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) - | ||
764 | REG_OFFSET); | ||
765 | if (!info->reg) | ||
766 | goto emapreg; | ||
767 | |||
768 | siu_i2s_dai.dev = &pdev->dev; | ||
769 | siu_i2s_dai.private_data = info; | ||
770 | |||
771 | ret = snd_soc_register_dais(&siu_i2s_dai, 1); | ||
772 | if (ret < 0) | ||
773 | goto edaiinit; | ||
774 | |||
775 | ret = snd_soc_register_platform(&siu_platform); | ||
776 | if (ret < 0) | ||
777 | goto esocregp; | ||
778 | |||
779 | pm_runtime_enable(&pdev->dev); | ||
780 | |||
781 | return ret; | ||
782 | |||
783 | esocregp: | ||
784 | snd_soc_unregister_dais(&siu_i2s_dai, 1); | ||
785 | edaiinit: | ||
786 | iounmap(info->reg); | ||
787 | emapreg: | ||
788 | iounmap(info->yram); | ||
789 | emapyram: | ||
790 | iounmap(info->xram); | ||
791 | emapxram: | ||
792 | iounmap(info->pram); | ||
793 | emappram: | ||
794 | release_mem_region(res->start, resource_size(res)); | ||
795 | ereqmemreg: | ||
796 | egetres: | ||
797 | ereqfw: | ||
798 | kfree(info); | ||
799 | |||
800 | return ret; | ||
801 | } | ||
802 | |||
803 | static int __devexit siu_remove(struct platform_device *pdev) | ||
804 | { | ||
805 | struct siu_info *info = siu_i2s_dai.private_data; | ||
806 | struct resource *res; | ||
807 | |||
808 | pm_runtime_disable(&pdev->dev); | ||
809 | |||
810 | snd_soc_unregister_platform(&siu_platform); | ||
811 | snd_soc_unregister_dais(&siu_i2s_dai, 1); | ||
812 | |||
813 | iounmap(info->reg); | ||
814 | iounmap(info->yram); | ||
815 | iounmap(info->xram); | ||
816 | iounmap(info->pram); | ||
817 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
818 | if (res) | ||
819 | release_mem_region(res->start, resource_size(res)); | ||
820 | kfree(info); | ||
821 | |||
822 | return 0; | ||
823 | } | ||
824 | |||
825 | static struct platform_driver siu_driver = { | ||
826 | .driver = { | ||
827 | .name = "sh_siu", | ||
828 | }, | ||
829 | .probe = siu_probe, | ||
830 | .remove = __devexit_p(siu_remove), | ||
831 | }; | ||
832 | |||
833 | static int __init siu_init(void) | ||
834 | { | ||
835 | return platform_driver_register(&siu_driver); | ||
836 | } | ||
837 | |||
838 | static void __exit siu_exit(void) | ||
839 | { | ||
840 | platform_driver_unregister(&siu_driver); | ||
841 | } | ||
842 | |||
843 | module_init(siu_init) | ||
844 | module_exit(siu_exit) | ||
845 | |||
846 | MODULE_AUTHOR("Carlos Munoz <carlos@kenati.com>"); | ||
847 | MODULE_DESCRIPTION("ALSA SoC SH7722 SIU driver"); | ||
848 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c new file mode 100644 index 000000000000..8f85719212f9 --- /dev/null +++ b/sound/soc/sh/siu_pcm.c | |||
@@ -0,0 +1,615 @@ | |||
1 | /* | ||
2 | * siu_pcm.c - ALSA driver for Renesas SH7343, SH7722 SIU peripheral. | ||
3 | * | ||
4 | * Copyright (C) 2009-2010 Guennadi Liakhovetski <g.liakhovetski@gmx.de> | ||
5 | * Copyright (C) 2006 Carlos Munoz <carlos@kenati.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License | ||
18 | * along with this program; if not, write to the Free Software | ||
19 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
20 | */ | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/dma-mapping.h> | ||
23 | #include <linux/dmaengine.h> | ||
24 | #include <linux/interrupt.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | |||
28 | #include <sound/control.h> | ||
29 | #include <sound/core.h> | ||
30 | #include <sound/pcm.h> | ||
31 | #include <sound/pcm_params.h> | ||
32 | #include <sound/soc-dai.h> | ||
33 | |||
34 | #include <asm/dmaengine.h> | ||
35 | #include <asm/siu.h> | ||
36 | |||
37 | #include "siu.h" | ||
38 | |||
39 | #define GET_MAX_PERIODS(buf_bytes, period_bytes) \ | ||
40 | ((buf_bytes) / (period_bytes)) | ||
41 | #define PERIOD_OFFSET(buf_addr, period_num, period_bytes) \ | ||
42 | ((buf_addr) + ((period_num) * (period_bytes))) | ||
43 | |||
44 | #define RWF_STM_RD 0x01 /* Read in progress */ | ||
45 | #define RWF_STM_WT 0x02 /* Write in progress */ | ||
46 | |||
47 | struct siu_port *siu_ports[SIU_PORT_NUM]; | ||
48 | |||
49 | /* transfersize is number of u32 dma transfers per period */ | ||
50 | static int siu_pcm_stmwrite_stop(struct siu_port *port_info) | ||
51 | { | ||
52 | struct siu_info *info = siu_i2s_dai.private_data; | ||
53 | u32 __iomem *base = info->reg; | ||
54 | struct siu_stream *siu_stream = &port_info->playback; | ||
55 | u32 stfifo; | ||
56 | |||
57 | if (!siu_stream->rw_flg) | ||
58 | return -EPERM; | ||
59 | |||
60 | /* output FIFO disable */ | ||
61 | stfifo = siu_read32(base + SIU_STFIFO); | ||
62 | siu_write32(base + SIU_STFIFO, stfifo & ~0x0c180c18); | ||
63 | pr_debug("%s: STFIFO %x -> %x\n", __func__, | ||
64 | stfifo, stfifo & ~0x0c180c18); | ||
65 | |||
66 | /* during stmwrite clear */ | ||
67 | siu_stream->rw_flg = 0; | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static int siu_pcm_stmwrite_start(struct siu_port *port_info) | ||
73 | { | ||
74 | struct siu_stream *siu_stream = &port_info->playback; | ||
75 | |||
76 | if (siu_stream->rw_flg) | ||
77 | return -EPERM; | ||
78 | |||
79 | /* Current period in buffer */ | ||
80 | port_info->playback.cur_period = 0; | ||
81 | |||
82 | /* during stmwrite flag set */ | ||
83 | siu_stream->rw_flg = RWF_STM_WT; | ||
84 | |||
85 | /* DMA transfer start */ | ||
86 | tasklet_schedule(&siu_stream->tasklet); | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void siu_dma_tx_complete(void *arg) | ||
92 | { | ||
93 | struct siu_stream *siu_stream = arg; | ||
94 | |||
95 | if (!siu_stream->rw_flg) | ||
96 | return; | ||
97 | |||
98 | /* Update completed period count */ | ||
99 | if (++siu_stream->cur_period >= | ||
100 | GET_MAX_PERIODS(siu_stream->buf_bytes, | ||
101 | siu_stream->period_bytes)) | ||
102 | siu_stream->cur_period = 0; | ||
103 | |||
104 | pr_debug("%s: done period #%d (%u/%u bytes), cookie %d\n", | ||
105 | __func__, siu_stream->cur_period, | ||
106 | siu_stream->cur_period * siu_stream->period_bytes, | ||
107 | siu_stream->buf_bytes, siu_stream->cookie); | ||
108 | |||
109 | tasklet_schedule(&siu_stream->tasklet); | ||
110 | |||
111 | /* Notify alsa: a period is done */ | ||
112 | snd_pcm_period_elapsed(siu_stream->substream); | ||
113 | } | ||
114 | |||
115 | static int siu_pcm_wr_set(struct siu_port *port_info, | ||
116 | dma_addr_t buff, u32 size) | ||
117 | { | ||
118 | struct siu_info *info = siu_i2s_dai.private_data; | ||
119 | u32 __iomem *base = info->reg; | ||
120 | struct siu_stream *siu_stream = &port_info->playback; | ||
121 | struct snd_pcm_substream *substream = siu_stream->substream; | ||
122 | struct device *dev = substream->pcm->card->dev; | ||
123 | struct dma_async_tx_descriptor *desc; | ||
124 | dma_cookie_t cookie; | ||
125 | struct scatterlist sg; | ||
126 | u32 stfifo; | ||
127 | |||
128 | sg_init_table(&sg, 1); | ||
129 | sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)), | ||
130 | size, offset_in_page(buff)); | ||
131 | sg_dma_address(&sg) = buff; | ||
132 | |||
133 | desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan, | ||
134 | &sg, 1, DMA_TO_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
135 | if (!desc) { | ||
136 | dev_err(dev, "Failed to allocate a dma descriptor\n"); | ||
137 | return -ENOMEM; | ||
138 | } | ||
139 | |||
140 | desc->callback = siu_dma_tx_complete; | ||
141 | desc->callback_param = siu_stream; | ||
142 | cookie = desc->tx_submit(desc); | ||
143 | if (cookie < 0) { | ||
144 | dev_err(dev, "Failed to submit a dma transfer\n"); | ||
145 | return cookie; | ||
146 | } | ||
147 | |||
148 | siu_stream->tx_desc = desc; | ||
149 | siu_stream->cookie = cookie; | ||
150 | |||
151 | dma_async_issue_pending(siu_stream->chan); | ||
152 | |||
153 | /* only output FIFO enable */ | ||
154 | stfifo = siu_read32(base + SIU_STFIFO); | ||
155 | siu_write32(base + SIU_STFIFO, stfifo | (port_info->stfifo & 0x0c180c18)); | ||
156 | dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, | ||
157 | stfifo, stfifo | (port_info->stfifo & 0x0c180c18)); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int siu_pcm_rd_set(struct siu_port *port_info, | ||
163 | dma_addr_t buff, size_t size) | ||
164 | { | ||
165 | struct siu_info *info = siu_i2s_dai.private_data; | ||
166 | u32 __iomem *base = info->reg; | ||
167 | struct siu_stream *siu_stream = &port_info->capture; | ||
168 | struct snd_pcm_substream *substream = siu_stream->substream; | ||
169 | struct device *dev = substream->pcm->card->dev; | ||
170 | struct dma_async_tx_descriptor *desc; | ||
171 | dma_cookie_t cookie; | ||
172 | struct scatterlist sg; | ||
173 | u32 stfifo; | ||
174 | |||
175 | dev_dbg(dev, "%s: %u@%llx\n", __func__, size, (unsigned long long)buff); | ||
176 | |||
177 | sg_init_table(&sg, 1); | ||
178 | sg_set_page(&sg, pfn_to_page(PFN_DOWN(buff)), | ||
179 | size, offset_in_page(buff)); | ||
180 | sg_dma_address(&sg) = buff; | ||
181 | |||
182 | desc = siu_stream->chan->device->device_prep_slave_sg(siu_stream->chan, | ||
183 | &sg, 1, DMA_FROM_DEVICE, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
184 | if (!desc) { | ||
185 | dev_err(dev, "Failed to allocate dma descriptor\n"); | ||
186 | return -ENOMEM; | ||
187 | } | ||
188 | |||
189 | desc->callback = siu_dma_tx_complete; | ||
190 | desc->callback_param = siu_stream; | ||
191 | cookie = desc->tx_submit(desc); | ||
192 | if (cookie < 0) { | ||
193 | dev_err(dev, "Failed to submit dma descriptor\n"); | ||
194 | return cookie; | ||
195 | } | ||
196 | |||
197 | siu_stream->tx_desc = desc; | ||
198 | siu_stream->cookie = cookie; | ||
199 | |||
200 | dma_async_issue_pending(siu_stream->chan); | ||
201 | |||
202 | /* only input FIFO enable */ | ||
203 | stfifo = siu_read32(base + SIU_STFIFO); | ||
204 | siu_write32(base + SIU_STFIFO, siu_read32(base + SIU_STFIFO) | | ||
205 | (port_info->stfifo & 0x13071307)); | ||
206 | dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, | ||
207 | stfifo, stfifo | (port_info->stfifo & 0x13071307)); | ||
208 | |||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | static void siu_io_tasklet(unsigned long data) | ||
213 | { | ||
214 | struct siu_stream *siu_stream = (struct siu_stream *)data; | ||
215 | struct snd_pcm_substream *substream = siu_stream->substream; | ||
216 | struct device *dev = substream->pcm->card->dev; | ||
217 | struct snd_pcm_runtime *rt = substream->runtime; | ||
218 | struct siu_port *port_info = siu_port_info(substream); | ||
219 | |||
220 | dev_dbg(dev, "%s: flags %x\n", __func__, siu_stream->rw_flg); | ||
221 | |||
222 | if (!siu_stream->rw_flg) { | ||
223 | dev_dbg(dev, "%s: stream inactive\n", __func__); | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { | ||
228 | dma_addr_t buff; | ||
229 | size_t count; | ||
230 | u8 *virt; | ||
231 | |||
232 | buff = (dma_addr_t)PERIOD_OFFSET(rt->dma_addr, | ||
233 | siu_stream->cur_period, | ||
234 | siu_stream->period_bytes); | ||
235 | virt = PERIOD_OFFSET(rt->dma_area, | ||
236 | siu_stream->cur_period, | ||
237 | siu_stream->period_bytes); | ||
238 | count = siu_stream->period_bytes; | ||
239 | |||
240 | /* DMA transfer start */ | ||
241 | siu_pcm_rd_set(port_info, buff, count); | ||
242 | } else { | ||
243 | siu_pcm_wr_set(port_info, | ||
244 | (dma_addr_t)PERIOD_OFFSET(rt->dma_addr, | ||
245 | siu_stream->cur_period, | ||
246 | siu_stream->period_bytes), | ||
247 | siu_stream->period_bytes); | ||
248 | } | ||
249 | } | ||
250 | |||
251 | /* Capture */ | ||
252 | static int siu_pcm_stmread_start(struct siu_port *port_info) | ||
253 | { | ||
254 | struct siu_stream *siu_stream = &port_info->capture; | ||
255 | |||
256 | if (siu_stream->xfer_cnt > 0x1000000) | ||
257 | return -EINVAL; | ||
258 | if (siu_stream->rw_flg) | ||
259 | return -EPERM; | ||
260 | |||
261 | /* Current period in buffer */ | ||
262 | siu_stream->cur_period = 0; | ||
263 | |||
264 | /* during stmread flag set */ | ||
265 | siu_stream->rw_flg = RWF_STM_RD; | ||
266 | |||
267 | tasklet_schedule(&siu_stream->tasklet); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int siu_pcm_stmread_stop(struct siu_port *port_info) | ||
273 | { | ||
274 | struct siu_info *info = siu_i2s_dai.private_data; | ||
275 | u32 __iomem *base = info->reg; | ||
276 | struct siu_stream *siu_stream = &port_info->capture; | ||
277 | struct device *dev = siu_stream->substream->pcm->card->dev; | ||
278 | u32 stfifo; | ||
279 | |||
280 | if (!siu_stream->rw_flg) | ||
281 | return -EPERM; | ||
282 | |||
283 | /* input FIFO disable */ | ||
284 | stfifo = siu_read32(base + SIU_STFIFO); | ||
285 | siu_write32(base + SIU_STFIFO, stfifo & ~0x13071307); | ||
286 | dev_dbg(dev, "%s: STFIFO %x -> %x\n", __func__, | ||
287 | stfifo, stfifo & ~0x13071307); | ||
288 | |||
289 | /* during stmread flag clear */ | ||
290 | siu_stream->rw_flg = 0; | ||
291 | |||
292 | return 0; | ||
293 | } | ||
294 | |||
295 | static int siu_pcm_hw_params(struct snd_pcm_substream *ss, | ||
296 | struct snd_pcm_hw_params *hw_params) | ||
297 | { | ||
298 | struct siu_info *info = siu_i2s_dai.private_data; | ||
299 | struct device *dev = ss->pcm->card->dev; | ||
300 | int ret; | ||
301 | |||
302 | dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); | ||
303 | |||
304 | ret = snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); | ||
305 | if (ret < 0) | ||
306 | dev_err(dev, "snd_pcm_lib_malloc_pages() failed\n"); | ||
307 | |||
308 | return ret; | ||
309 | } | ||
310 | |||
311 | static int siu_pcm_hw_free(struct snd_pcm_substream *ss) | ||
312 | { | ||
313 | struct siu_info *info = siu_i2s_dai.private_data; | ||
314 | struct siu_port *port_info = siu_port_info(ss); | ||
315 | struct device *dev = ss->pcm->card->dev; | ||
316 | struct siu_stream *siu_stream; | ||
317 | |||
318 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
319 | siu_stream = &port_info->playback; | ||
320 | else | ||
321 | siu_stream = &port_info->capture; | ||
322 | |||
323 | dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); | ||
324 | |||
325 | return snd_pcm_lib_free_pages(ss); | ||
326 | } | ||
327 | |||
328 | static bool filter(struct dma_chan *chan, void *slave) | ||
329 | { | ||
330 | struct sh_dmae_slave *param = slave; | ||
331 | |||
332 | pr_debug("%s: slave ID %d\n", __func__, param->slave_id); | ||
333 | |||
334 | if (unlikely(param->dma_dev != chan->device->dev)) | ||
335 | return false; | ||
336 | |||
337 | chan->private = param; | ||
338 | return true; | ||
339 | } | ||
340 | |||
341 | static int siu_pcm_open(struct snd_pcm_substream *ss) | ||
342 | { | ||
343 | /* Playback / Capture */ | ||
344 | struct siu_info *info = siu_i2s_dai.private_data; | ||
345 | struct siu_port *port_info = siu_port_info(ss); | ||
346 | struct siu_stream *siu_stream; | ||
347 | u32 port = info->port_id; | ||
348 | struct siu_platform *pdata = siu_i2s_dai.dev->platform_data; | ||
349 | struct device *dev = ss->pcm->card->dev; | ||
350 | dma_cap_mask_t mask; | ||
351 | struct sh_dmae_slave *param; | ||
352 | |||
353 | dma_cap_zero(mask); | ||
354 | dma_cap_set(DMA_SLAVE, mask); | ||
355 | |||
356 | dev_dbg(dev, "%s, port=%d@%p\n", __func__, port, port_info); | ||
357 | |||
358 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
359 | siu_stream = &port_info->playback; | ||
360 | param = &siu_stream->param; | ||
361 | param->slave_id = port ? SHDMA_SLAVE_SIUB_TX : | ||
362 | SHDMA_SLAVE_SIUA_TX; | ||
363 | } else { | ||
364 | siu_stream = &port_info->capture; | ||
365 | param = &siu_stream->param; | ||
366 | param->slave_id = port ? SHDMA_SLAVE_SIUB_RX : | ||
367 | SHDMA_SLAVE_SIUA_RX; | ||
368 | } | ||
369 | |||
370 | param->dma_dev = pdata->dma_dev; | ||
371 | /* Get DMA channel */ | ||
372 | siu_stream->chan = dma_request_channel(mask, filter, param); | ||
373 | if (!siu_stream->chan) { | ||
374 | dev_err(dev, "DMA channel allocation failed!\n"); | ||
375 | return -EBUSY; | ||
376 | } | ||
377 | |||
378 | siu_stream->substream = ss; | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int siu_pcm_close(struct snd_pcm_substream *ss) | ||
384 | { | ||
385 | struct siu_info *info = siu_i2s_dai.private_data; | ||
386 | struct device *dev = ss->pcm->card->dev; | ||
387 | struct siu_port *port_info = siu_port_info(ss); | ||
388 | struct siu_stream *siu_stream; | ||
389 | |||
390 | dev_dbg(dev, "%s: port=%d\n", __func__, info->port_id); | ||
391 | |||
392 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
393 | siu_stream = &port_info->playback; | ||
394 | else | ||
395 | siu_stream = &port_info->capture; | ||
396 | |||
397 | dma_release_channel(siu_stream->chan); | ||
398 | siu_stream->chan = NULL; | ||
399 | |||
400 | siu_stream->substream = NULL; | ||
401 | |||
402 | return 0; | ||
403 | } | ||
404 | |||
405 | static int siu_pcm_prepare(struct snd_pcm_substream *ss) | ||
406 | { | ||
407 | struct siu_info *info = siu_i2s_dai.private_data; | ||
408 | struct siu_port *port_info = siu_port_info(ss); | ||
409 | struct device *dev = ss->pcm->card->dev; | ||
410 | struct snd_pcm_runtime *rt = ss->runtime; | ||
411 | struct siu_stream *siu_stream; | ||
412 | snd_pcm_sframes_t xfer_cnt; | ||
413 | |||
414 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
415 | siu_stream = &port_info->playback; | ||
416 | else | ||
417 | siu_stream = &port_info->capture; | ||
418 | |||
419 | rt = siu_stream->substream->runtime; | ||
420 | |||
421 | siu_stream->buf_bytes = snd_pcm_lib_buffer_bytes(ss); | ||
422 | siu_stream->period_bytes = snd_pcm_lib_period_bytes(ss); | ||
423 | |||
424 | dev_dbg(dev, "%s: port=%d, %d channels, period=%u bytes\n", __func__, | ||
425 | info->port_id, rt->channels, siu_stream->period_bytes); | ||
426 | |||
427 | /* We only support buffers that are multiples of the period */ | ||
428 | if (siu_stream->buf_bytes % siu_stream->period_bytes) { | ||
429 | dev_err(dev, "%s() - buffer=%d not multiple of period=%d\n", | ||
430 | __func__, siu_stream->buf_bytes, | ||
431 | siu_stream->period_bytes); | ||
432 | return -EINVAL; | ||
433 | } | ||
434 | |||
435 | xfer_cnt = bytes_to_frames(rt, siu_stream->period_bytes); | ||
436 | if (!xfer_cnt || xfer_cnt > 0x1000000) | ||
437 | return -EINVAL; | ||
438 | |||
439 | siu_stream->format = rt->format; | ||
440 | siu_stream->xfer_cnt = xfer_cnt; | ||
441 | |||
442 | dev_dbg(dev, "port=%d buf=%lx buf_bytes=%d period_bytes=%d " | ||
443 | "format=%d channels=%d xfer_cnt=%d\n", info->port_id, | ||
444 | (unsigned long)rt->dma_addr, siu_stream->buf_bytes, | ||
445 | siu_stream->period_bytes, | ||
446 | siu_stream->format, rt->channels, (int)xfer_cnt); | ||
447 | |||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static int siu_pcm_trigger(struct snd_pcm_substream *ss, int cmd) | ||
452 | { | ||
453 | struct siu_info *info = siu_i2s_dai.private_data; | ||
454 | struct device *dev = ss->pcm->card->dev; | ||
455 | struct siu_port *port_info = siu_port_info(ss); | ||
456 | int ret; | ||
457 | |||
458 | dev_dbg(dev, "%s: port=%d@%p, cmd=%d\n", __func__, | ||
459 | info->port_id, port_info, cmd); | ||
460 | |||
461 | switch (cmd) { | ||
462 | case SNDRV_PCM_TRIGGER_START: | ||
463 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
464 | ret = siu_pcm_stmwrite_start(port_info); | ||
465 | else | ||
466 | ret = siu_pcm_stmread_start(port_info); | ||
467 | |||
468 | if (ret < 0) | ||
469 | dev_warn(dev, "%s: start failed on port=%d\n", | ||
470 | __func__, info->port_id); | ||
471 | |||
472 | break; | ||
473 | case SNDRV_PCM_TRIGGER_STOP: | ||
474 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
475 | siu_pcm_stmwrite_stop(port_info); | ||
476 | else | ||
477 | siu_pcm_stmread_stop(port_info); | ||
478 | ret = 0; | ||
479 | |||
480 | break; | ||
481 | default: | ||
482 | dev_err(dev, "%s() unsupported cmd=%d\n", __func__, cmd); | ||
483 | ret = -EINVAL; | ||
484 | } | ||
485 | |||
486 | return ret; | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * So far only resolution of one period is supported, subject to extending the | ||
491 | * dmangine API | ||
492 | */ | ||
493 | static snd_pcm_uframes_t siu_pcm_pointer_dma(struct snd_pcm_substream *ss) | ||
494 | { | ||
495 | struct device *dev = ss->pcm->card->dev; | ||
496 | struct siu_info *info = siu_i2s_dai.private_data; | ||
497 | u32 __iomem *base = info->reg; | ||
498 | struct siu_port *port_info = siu_port_info(ss); | ||
499 | struct snd_pcm_runtime *rt = ss->runtime; | ||
500 | size_t ptr; | ||
501 | struct siu_stream *siu_stream; | ||
502 | |||
503 | if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
504 | siu_stream = &port_info->playback; | ||
505 | else | ||
506 | siu_stream = &port_info->capture; | ||
507 | |||
508 | /* | ||
509 | * ptr is the offset into the buffer where the dma is currently at. We | ||
510 | * check if the dma buffer has just wrapped. | ||
511 | */ | ||
512 | ptr = PERIOD_OFFSET(rt->dma_addr, | ||
513 | siu_stream->cur_period, | ||
514 | siu_stream->period_bytes) - rt->dma_addr; | ||
515 | |||
516 | dev_dbg(dev, | ||
517 | "%s: port=%d, events %x, FSTS %x, xferred %u/%u, cookie %d\n", | ||
518 | __func__, info->port_id, siu_read32(base + SIU_EVNTC), | ||
519 | siu_read32(base + SIU_SBFSTS), ptr, siu_stream->buf_bytes, | ||
520 | siu_stream->cookie); | ||
521 | |||
522 | if (ptr >= siu_stream->buf_bytes) | ||
523 | ptr = 0; | ||
524 | |||
525 | return bytes_to_frames(ss->runtime, ptr); | ||
526 | } | ||
527 | |||
528 | static int siu_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, | ||
529 | struct snd_pcm *pcm) | ||
530 | { | ||
531 | /* card->dev == socdev->dev, see snd_soc_new_pcms() */ | ||
532 | struct siu_info *info = siu_i2s_dai.private_data; | ||
533 | struct platform_device *pdev = to_platform_device(card->dev); | ||
534 | int ret; | ||
535 | int i; | ||
536 | |||
537 | /* pdev->id selects between SIUA and SIUB */ | ||
538 | if (pdev->id < 0 || pdev->id >= SIU_PORT_NUM) | ||
539 | return -EINVAL; | ||
540 | |||
541 | info->port_id = pdev->id; | ||
542 | |||
543 | /* | ||
544 | * While the siu has 2 ports, only one port can be on at a time (only 1 | ||
545 | * SPB). So far all the boards using the siu had only one of the ports | ||
546 | * wired to a codec. To simplify things, we only register one port with | ||
547 | * alsa. In case both ports are needed, it should be changed here | ||
548 | */ | ||
549 | for (i = pdev->id; i < pdev->id + 1; i++) { | ||
550 | struct siu_port **port_info = &siu_ports[i]; | ||
551 | |||
552 | ret = siu_init_port(i, port_info, card); | ||
553 | if (ret < 0) | ||
554 | return ret; | ||
555 | |||
556 | ret = snd_pcm_lib_preallocate_pages_for_all(pcm, | ||
557 | SNDRV_DMA_TYPE_DEV, NULL, | ||
558 | SIU_BUFFER_BYTES_MAX, SIU_BUFFER_BYTES_MAX); | ||
559 | if (ret < 0) { | ||
560 | dev_err(card->dev, | ||
561 | "snd_pcm_lib_preallocate_pages_for_all() err=%d", | ||
562 | ret); | ||
563 | goto fail; | ||
564 | } | ||
565 | |||
566 | (*port_info)->pcm = pcm; | ||
567 | |||
568 | /* IO tasklets */ | ||
569 | tasklet_init(&(*port_info)->playback.tasklet, siu_io_tasklet, | ||
570 | (unsigned long)&(*port_info)->playback); | ||
571 | tasklet_init(&(*port_info)->capture.tasklet, siu_io_tasklet, | ||
572 | (unsigned long)&(*port_info)->capture); | ||
573 | } | ||
574 | |||
575 | dev_info(card->dev, "SuperH SIU driver initialized.\n"); | ||
576 | return 0; | ||
577 | |||
578 | fail: | ||
579 | siu_free_port(siu_ports[pdev->id]); | ||
580 | dev_err(card->dev, "SIU: failed to initialize.\n"); | ||
581 | return ret; | ||
582 | } | ||
583 | |||
584 | static void siu_pcm_free(struct snd_pcm *pcm) | ||
585 | { | ||
586 | struct platform_device *pdev = to_platform_device(pcm->card->dev); | ||
587 | struct siu_port *port_info = siu_ports[pdev->id]; | ||
588 | |||
589 | tasklet_kill(&port_info->capture.tasklet); | ||
590 | tasklet_kill(&port_info->playback.tasklet); | ||
591 | |||
592 | siu_free_port(port_info); | ||
593 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
594 | |||
595 | dev_dbg(pcm->card->dev, "%s\n", __func__); | ||
596 | } | ||
597 | |||
598 | static struct snd_pcm_ops siu_pcm_ops = { | ||
599 | .open = siu_pcm_open, | ||
600 | .close = siu_pcm_close, | ||
601 | .ioctl = snd_pcm_lib_ioctl, | ||
602 | .hw_params = siu_pcm_hw_params, | ||
603 | .hw_free = siu_pcm_hw_free, | ||
604 | .prepare = siu_pcm_prepare, | ||
605 | .trigger = siu_pcm_trigger, | ||
606 | .pointer = siu_pcm_pointer_dma, | ||
607 | }; | ||
608 | |||
609 | struct snd_soc_platform siu_platform = { | ||
610 | .name = "siu-audio", | ||
611 | .pcm_ops = &siu_pcm_ops, | ||
612 | .pcm_new = siu_pcm_new, | ||
613 | .pcm_free = siu_pcm_free, | ||
614 | }; | ||
615 | EXPORT_SYMBOL_GPL(siu_platform); | ||