aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
authorLiam Girdwood <lrg@slimlogic.co.uk>2010-03-17 16:15:21 -0400
committerLiam Girdwood <lrg@slimlogic.co.uk>2010-08-12 09:00:00 -0400
commitf0fba2ad1b6b53d5360125c41953b7afcd6deff0 (patch)
treef6ad50905f8daa616593c978d7ae992e73241180 /sound/soc/codecs/twl4030.c
parentbda7d2a862e6b788bca2d02d38a07966a9c92e48 (diff)
ASoC: multi-component - ASoC Multi-Component Support
This patch extends the ASoC API to allow sound cards to have more than one CODEC and more than one platform DMA controller. This is achieved by dividing some current ASoC structures that contain both driver data and device data into structures that only either contain device data or driver data. i.e. struct snd_soc_codec ---> struct snd_soc_codec (device data) +-> struct snd_soc_codec_driver (driver data) struct snd_soc_platform ---> struct snd_soc_platform (device data) +-> struct snd_soc_platform_driver (driver data) struct snd_soc_dai ---> struct snd_soc_dai (device data) +-> struct snd_soc_dai_driver (driver data) struct snd_soc_device ---> deleted This now allows ASoC to be more tightly aligned with the Linux driver model and also means that every ASoC codec, platform and (platform) DAI is a kernel device. ASoC component private data is now stored as device private data. The ASoC sound card struct snd_soc_card has also been updated to store lists of it's components rather than a pointer to a codec and platform. The PCM runtime struct soc_pcm_runtime now has pointers to all its components. This patch adds DAPM support for ASoC multi-component and removes struct snd_soc_socdev from DAPM core. All DAPM calls are now made on a card, codec or runtime PCM level basis rather than using snd_soc_socdev. Other notable multi-component changes:- * Stream operations now de-reference less structures. * close_delayed work() now runs on a DAI basis rather than looping all DAIs in a card. * PM suspend()/resume() operations can now handle N CODECs and Platforms per sound card. * Added soc_bind_dai_link() to bind the component devices to the sound card. * Added soc_dai_link_probe() and soc_dai_link_remove() to probe and remove DAI link components. * sysfs entries can now be registered per component per card. * snd_soc_new_pcms() functionailty rolled into dai_link_probe(). * snd_soc_register_codec() now does all the codec list and mutex init. This patch changes the probe() and remove() of the CODEC drivers as follows:- o Make CODEC driver a platform driver o Moved all struct snd_soc_codec list, mutex, etc initialiasation to core. o Removed all static codec pointers (drivers now support > 1 codec dev) o snd_soc_register_pcms() now done by core. o snd_soc_register_dai() folded into snd_soc_register_codec(). CS4270 portions: Acked-by: Timur Tabi <timur@freescale.com> Some TLV320aic23 and Cirrus platform fixes. Signed-off-by: Ryan Mallon <ryan@bluewatersys.com> TI CODEC and OMAP fixes Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Jarkko Nikula <jhnikula@gmail.com> Samsung platform and misc fixes :- Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com> Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Reviewed-by: Jassi Brar <jassi.brar@samsung.com> Signed-off-by: Seungwhan Youn <sw.youn@samsung.com> MPC8610 and PPC fixes. Signed-off-by: Timur Tabi <timur@freescale.com> i.MX fixes and some core fixes. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> J4740 platform fixes:- Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> CC: Tony Lindgren <tony@atomide.com> CC: Nicolas Ferre <nicolas.ferre@atmel.com> CC: Kevin Hilman <khilman@deeprootsystems.com> CC: Sascha Hauer <s.hauer@pengutronix.de> CC: Atsushi Nemoto <anemo@mba.ocn.ne.jp> CC: Kuninori Morimoto <morimoto.kuninori@renesas.com> CC: Daniel Gloeckner <dg@emlix.com> CC: Manuel Lauss <mano@roarinelk.homelinux.net> CC: Mike Frysinger <vapier.adi@gmail.com> CC: Arnaud Patard <apatard@mandriva.com> CC: Wan ZongShun <mcuos.com@gmail.com> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r--sound/soc/codecs/twl4030.c231
1 files changed, 71 insertions, 160 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 7b618bbff884..898430f44f9d 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -36,7 +36,16 @@
36#include <sound/initval.h> 36#include <sound/initval.h>
37#include <sound/tlv.h> 37#include <sound/tlv.h>
38 38
39#include "twl4030.h" 39/* Register descriptions are here */
40#include <linux/mfd/twl4030-codec.h>
41
42/* Shadow register used by the audio driver */
43#define TWL4030_REG_SW_SHADOW 0x4A
44#define TWL4030_CACHEREGNUM (TWL4030_REG_SW_SHADOW + 1)
45
46/* TWL4030_REG_SW_SHADOW (0x4A) Fields */
47#define TWL4030_HFL_EN 0x01
48#define TWL4030_HFR_EN 0x02
40 49
41/* 50/*
42 * twl4030 register cache & default register settings 51 * twl4030 register cache & default register settings
@@ -277,21 +286,19 @@ static inline void twl4030_reset_registers(struct snd_soc_codec *codec)
277 286
278} 287}
279 288
280static void twl4030_init_chip(struct platform_device *pdev) 289static void twl4030_init_chip(struct snd_soc_codec *codec)
281{ 290{
282 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 291 struct twl4030_codec_audio_data *pdata = dev_get_platdata(codec->dev);
283 struct twl4030_setup_data *setup = socdev->codec_data;
284 struct snd_soc_codec *codec = socdev->card->codec;
285 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 292 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
286 u8 reg, byte; 293 u8 reg, byte;
287 int i = 0; 294 int i = 0;
288 295
289 /* Check defaults, if instructed before anything else */ 296 /* Check defaults, if instructed before anything else */
290 if (setup && setup->check_defaults) 297 if (pdata && pdata->check_defaults)
291 twl4030_check_defaults(codec); 298 twl4030_check_defaults(codec);
292 299
293 /* Reset registers, if no setup data or if instructed to do so */ 300 /* Reset registers, if no setup data or if instructed to do so */
294 if (!setup || (setup && setup->reset_registers)) 301 if (!pdata || (pdata && pdata->reset_registers))
295 twl4030_reset_registers(codec); 302 twl4030_reset_registers(codec);
296 303
297 /* Refresh APLL_CTL register from HW */ 304 /* Refresh APLL_CTL register from HW */
@@ -312,20 +319,14 @@ static void twl4030_init_chip(struct platform_device *pdev)
312 twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32); 319 twl4030_write(codec, TWL4030_REG_ARXR2_APGA_CTL, 0x32);
313 320
314 /* Machine dependent setup */ 321 /* Machine dependent setup */
315 if (!setup) 322 if (!pdata)
316 return; 323 return;
317 324
318 twl4030->digimic_delay = setup->digimic_delay; 325 twl4030->digimic_delay = pdata->digimic_delay;
319
320 /* Configuration for headset ramp delay from setup data */
321 if (setup->sysclk != twl4030->sysclk)
322 dev_warn(codec->dev,
323 "Mismatch in APLL mclk: %u (configured: %u)\n",
324 setup->sysclk, twl4030->sysclk);
325 326
326 reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET); 327 reg = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
327 reg &= ~TWL4030_RAMP_DELAY; 328 reg &= ~TWL4030_RAMP_DELAY;
328 reg |= (setup->ramp_delay_value << 2); 329 reg |= (pdata->ramp_delay_value << 2);
329 twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg); 330 twl4030_write_reg_cache(codec, TWL4030_REG_HS_POPN_SET, reg);
330 331
331 /* initiate offset cancellation */ 332 /* initiate offset cancellation */
@@ -333,7 +334,7 @@ static void twl4030_init_chip(struct platform_device *pdev)
333 334
334 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL); 335 reg = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
335 reg &= ~TWL4030_OFFSET_CNCL_SEL; 336 reg &= ~TWL4030_OFFSET_CNCL_SEL;
336 reg |= setup->offset_cncl_path; 337 reg |= pdata->offset_cncl_path;
337 twl4030_write(codec, TWL4030_REG_ANAMICL, 338 twl4030_write(codec, TWL4030_REG_ANAMICL,
338 reg | TWL4030_CNCL_OFFSET_START); 339 reg | TWL4030_CNCL_OFFSET_START);
339 340
@@ -718,9 +719,7 @@ static int aif_event(struct snd_soc_dapm_widget *w,
718 719
719static void headset_ramp(struct snd_soc_codec *codec, int ramp) 720static void headset_ramp(struct snd_soc_codec *codec, int ramp)
720{ 721{
721 struct snd_soc_device *socdev = codec->socdev; 722 struct twl4030_codec_audio_data *pdata = codec->dev->platform_data;
722 struct twl4030_setup_data *setup = socdev->codec_data;
723
724 unsigned char hs_gain, hs_pop; 723 unsigned char hs_gain, hs_pop;
725 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 724 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
726 /* Base values for ramp delay calculation: 2^19 - 2^26 */ 725 /* Base values for ramp delay calculation: 2^19 - 2^26 */
@@ -732,9 +731,9 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
732 731
733 /* Enable external mute control, this dramatically reduces 732 /* Enable external mute control, this dramatically reduces
734 * the pop-noise */ 733 * the pop-noise */
735 if (setup && setup->hs_extmute) { 734 if (pdata && pdata->hs_extmute) {
736 if (setup->set_hs_extmute) { 735 if (pdata->set_hs_extmute) {
737 setup->set_hs_extmute(1); 736 pdata->set_hs_extmute(1);
738 } else { 737 } else {
739 hs_pop |= TWL4030_EXTMUTE; 738 hs_pop |= TWL4030_EXTMUTE;
740 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); 739 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
@@ -772,9 +771,9 @@ static void headset_ramp(struct snd_soc_codec *codec, int ramp)
772 } 771 }
773 772
774 /* Disable external mute */ 773 /* Disable external mute */
775 if (setup && setup->hs_extmute) { 774 if (pdata && pdata->hs_extmute) {
776 if (setup->set_hs_extmute) { 775 if (pdata->set_hs_extmute) {
777 setup->set_hs_extmute(0); 776 pdata->set_hs_extmute(0);
778 } else { 777 } else {
779 hs_pop &= ~TWL4030_EXTMUTE; 778 hs_pop &= ~TWL4030_EXTMUTE;
780 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop); 779 twl4030_write(codec, TWL4030_REG_HS_POPN_SET, hs_pop);
@@ -1707,8 +1706,7 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
1707 struct snd_soc_dai *dai) 1706 struct snd_soc_dai *dai)
1708{ 1707{
1709 struct snd_soc_pcm_runtime *rtd = substream->private_data; 1708 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1710 struct snd_soc_device *socdev = rtd->socdev; 1709 struct snd_soc_codec *codec = rtd->codec;
1711 struct snd_soc_codec *codec = socdev->card->codec;
1712 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 1710 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
1713 1711
1714 if (twl4030->master_substream) { 1712 if (twl4030->master_substream) {
@@ -1738,8 +1736,7 @@ static void twl4030_shutdown(struct snd_pcm_substream *substream,
1738 struct snd_soc_dai *dai) 1736 struct snd_soc_dai *dai)
1739{ 1737{
1740 struct snd_soc_pcm_runtime *rtd = substream->private_data; 1738 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1741 struct snd_soc_device *socdev = rtd->socdev; 1739 struct snd_soc_codec *codec = rtd->codec;
1742 struct snd_soc_codec *codec = socdev->card->codec;
1743 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 1740 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
1744 1741
1745 if (twl4030->master_substream == substream) 1742 if (twl4030->master_substream == substream)
@@ -1764,8 +1761,7 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
1764 struct snd_soc_dai *dai) 1761 struct snd_soc_dai *dai)
1765{ 1762{
1766 struct snd_soc_pcm_runtime *rtd = substream->private_data; 1763 struct snd_soc_pcm_runtime *rtd = substream->private_data;
1767 struct snd_soc_device *socdev = rtd->socdev; 1764 struct snd_soc_codec *codec = rtd->codec;
1768 struct snd_soc_codec *codec = socdev->card->codec;
1769 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 1765 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
1770 u8 mode, old_mode, format, old_format; 1766 u8 mode, old_mode, format, old_format;
1771 1767
@@ -1999,8 +1995,7 @@ static int twl4030_voice_startup(struct snd_pcm_substream *substream,
1999 struct snd_soc_dai *dai) 1995 struct snd_soc_dai *dai)
2000{ 1996{
2001 struct snd_soc_pcm_runtime *rtd = substream->private_data; 1997 struct snd_soc_pcm_runtime *rtd = substream->private_data;
2002 struct snd_soc_device *socdev = rtd->socdev; 1998 struct snd_soc_codec *codec = rtd->codec;
2003 struct snd_soc_codec *codec = socdev->card->codec;
2004 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 1999 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
2005 u8 mode; 2000 u8 mode;
2006 2001
@@ -2033,8 +2028,7 @@ static void twl4030_voice_shutdown(struct snd_pcm_substream *substream,
2033 struct snd_soc_dai *dai) 2028 struct snd_soc_dai *dai)
2034{ 2029{
2035 struct snd_soc_pcm_runtime *rtd = substream->private_data; 2030 struct snd_soc_pcm_runtime *rtd = substream->private_data;
2036 struct snd_soc_device *socdev = rtd->socdev; 2031 struct snd_soc_codec *codec = rtd->codec;
2037 struct snd_soc_codec *codec = socdev->card->codec;
2038 2032
2039 /* Enable voice digital filters */ 2033 /* Enable voice digital filters */
2040 twl4030_voice_enable(codec, substream->stream, 0); 2034 twl4030_voice_enable(codec, substream->stream, 0);
@@ -2044,8 +2038,7 @@ static int twl4030_voice_hw_params(struct snd_pcm_substream *substream,
2044 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 2038 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
2045{ 2039{
2046 struct snd_soc_pcm_runtime *rtd = substream->private_data; 2040 struct snd_soc_pcm_runtime *rtd = substream->private_data;
2047 struct snd_soc_device *socdev = rtd->socdev; 2041 struct snd_soc_codec *codec = rtd->codec;
2048 struct snd_soc_codec *codec = socdev->card->codec;
2049 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec); 2042 struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
2050 u8 old_mode, mode; 2043 u8 old_mode, mode;
2051 2044
@@ -2175,7 +2168,7 @@ static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
2175#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000) 2168#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
2176#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE) 2169#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
2177 2170
2178static struct snd_soc_dai_ops twl4030_dai_ops = { 2171static struct snd_soc_dai_ops twl4030_dai_hifi_ops = {
2179 .startup = twl4030_startup, 2172 .startup = twl4030_startup,
2180 .shutdown = twl4030_shutdown, 2173 .shutdown = twl4030_shutdown,
2181 .hw_params = twl4030_hw_params, 2174 .hw_params = twl4030_hw_params,
@@ -2193,9 +2186,9 @@ static struct snd_soc_dai_ops twl4030_dai_voice_ops = {
2193 .set_tristate = twl4030_voice_set_tristate, 2186 .set_tristate = twl4030_voice_set_tristate,
2194}; 2187};
2195 2188
2196struct snd_soc_dai twl4030_dai[] = { 2189static struct snd_soc_dai_driver twl4030_dai[] = {
2197{ 2190{
2198 .name = "twl4030", 2191 .name = "twl4030-hifi",
2199 .playback = { 2192 .playback = {
2200 .stream_name = "HiFi Playback", 2193 .stream_name = "HiFi Playback",
2201 .channels_min = 2, 2194 .channels_min = 2,
@@ -2208,10 +2201,10 @@ struct snd_soc_dai twl4030_dai[] = {
2208 .channels_max = 4, 2201 .channels_max = 4,
2209 .rates = TWL4030_RATES, 2202 .rates = TWL4030_RATES,
2210 .formats = TWL4030_FORMATS,}, 2203 .formats = TWL4030_FORMATS,},
2211 .ops = &twl4030_dai_ops, 2204 .ops = &twl4030_dai_hifi_ops,
2212}, 2205},
2213{ 2206{
2214 .name = "twl4030 Voice", 2207 .name = "twl4030-voice",
2215 .playback = { 2208 .playback = {
2216 .stream_name = "Voice Playback", 2209 .stream_name = "Voice Playback",
2217 .channels_min = 1, 2210 .channels_min = 1,
@@ -2227,164 +2220,90 @@ struct snd_soc_dai twl4030_dai[] = {
2227 .ops = &twl4030_dai_voice_ops, 2220 .ops = &twl4030_dai_voice_ops,
2228}, 2221},
2229}; 2222};
2230EXPORT_SYMBOL_GPL(twl4030_dai);
2231 2223
2232static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state) 2224static int twl4030_soc_suspend(struct snd_soc_codec *codec, pm_message_t state)
2233{ 2225{
2234 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2235 struct snd_soc_codec *codec = socdev->card->codec;
2236
2237 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); 2226 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
2238
2239 return 0; 2227 return 0;
2240} 2228}
2241 2229
2242static int twl4030_soc_resume(struct platform_device *pdev) 2230static int twl4030_soc_resume(struct snd_soc_codec *codec)
2243{ 2231{
2244 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2245 struct snd_soc_codec *codec = socdev->card->codec;
2246
2247 twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 2232 twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
2248 return 0; 2233 return 0;
2249} 2234}
2250 2235
2251static struct snd_soc_codec *twl4030_codec; 2236static int twl4030_soc_probe(struct snd_soc_codec *codec)
2252
2253static int twl4030_soc_probe(struct platform_device *pdev)
2254{ 2237{
2255 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 2238 struct twl4030_priv *twl4030;
2256 struct snd_soc_codec *codec;
2257 int ret;
2258
2259 BUG_ON(!twl4030_codec);
2260
2261 codec = twl4030_codec;
2262 socdev->card->codec = codec;
2263
2264 twl4030_init_chip(pdev);
2265 2239
2266 /* register pcms */ 2240 twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
2267 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 2241 if (twl4030 == NULL) {
2268 if (ret < 0) { 2242 printk("Can not allocate memroy\n");
2269 dev_err(&pdev->dev, "failed to create pcms\n"); 2243 return -ENOMEM;
2270 return ret;
2271 } 2244 }
2245 snd_soc_codec_set_drvdata(codec, twl4030);
2246 /* Set the defaults, and power up the codec */
2247 twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
2248 codec->bias_level = SND_SOC_BIAS_OFF;
2249 codec->idle_bias_off = 1;
2250
2251 twl4030_init_chip(codec);
2272 2252
2273 snd_soc_add_controls(codec, twl4030_snd_controls, 2253 snd_soc_add_controls(codec, twl4030_snd_controls,
2274 ARRAY_SIZE(twl4030_snd_controls)); 2254 ARRAY_SIZE(twl4030_snd_controls));
2275 twl4030_add_widgets(codec); 2255 twl4030_add_widgets(codec);
2276
2277 return 0; 2256 return 0;
2278} 2257}
2279 2258
2280static int twl4030_soc_remove(struct platform_device *pdev) 2259static int twl4030_soc_remove(struct snd_soc_codec *codec)
2281{ 2260{
2282 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2283 struct snd_soc_codec *codec = socdev->card->codec;
2284
2285 /* Reset registers to their chip default before leaving */
2286 twl4030_reset_registers(codec);
2287 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF); 2261 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
2288 snd_soc_free_pcms(socdev);
2289 snd_soc_dapm_free(socdev);
2290
2291 return 0; 2262 return 0;
2292} 2263}
2293 2264
2265static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
2266 .probe = twl4030_soc_probe,
2267 .remove = twl4030_soc_remove,
2268 .suspend = twl4030_soc_suspend,
2269 .resume = twl4030_soc_resume,
2270 .read = twl4030_read_reg_cache,
2271 .write = twl4030_write,
2272 .set_bias_level = twl4030_set_bias_level,
2273 .reg_cache_size = sizeof(twl4030_reg),
2274 .reg_word_size = sizeof(u8),
2275 .reg_cache_default = twl4030_reg,
2276};
2277
2294static int __devinit twl4030_codec_probe(struct platform_device *pdev) 2278static int __devinit twl4030_codec_probe(struct platform_device *pdev)
2295{ 2279{
2296 struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data; 2280 struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
2297 struct snd_soc_codec *codec;
2298 struct twl4030_priv *twl4030;
2299 int ret;
2300 2281
2301 if (!pdata) { 2282 if (!pdata) {
2302 dev_err(&pdev->dev, "platform_data is missing\n"); 2283 dev_err(&pdev->dev, "platform_data is missing\n");
2303 return -EINVAL; 2284 return -EINVAL;
2304 } 2285 }
2305 2286
2306 twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); 2287 return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_twl4030,
2307 if (twl4030 == NULL) { 2288 twl4030_dai, ARRAY_SIZE(twl4030_dai));
2308 dev_err(&pdev->dev, "Can not allocate memroy\n");
2309 return -ENOMEM;
2310 }
2311
2312 codec = &twl4030->codec;
2313 snd_soc_codec_set_drvdata(codec, twl4030);
2314 codec->dev = &pdev->dev;
2315 twl4030_dai[0].dev = &pdev->dev;
2316 twl4030_dai[1].dev = &pdev->dev;
2317
2318 mutex_init(&codec->mutex);
2319 INIT_LIST_HEAD(&codec->dapm_widgets);
2320 INIT_LIST_HEAD(&codec->dapm_paths);
2321
2322 codec->name = "twl4030";
2323 codec->owner = THIS_MODULE;
2324 codec->read = twl4030_read_reg_cache;
2325 codec->write = twl4030_write;
2326 codec->set_bias_level = twl4030_set_bias_level;
2327 codec->idle_bias_off = 1;
2328 codec->dai = twl4030_dai;
2329 codec->num_dai = ARRAY_SIZE(twl4030_dai);
2330 codec->reg_cache_size = sizeof(twl4030_reg);
2331 codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
2332 GFP_KERNEL);
2333 if (codec->reg_cache == NULL) {
2334 ret = -ENOMEM;
2335 goto error_cache;
2336 }
2337
2338 platform_set_drvdata(pdev, twl4030);
2339 twl4030_codec = codec;
2340
2341 /* Set the defaults, and power up the codec */
2342 twl4030->sysclk = twl4030_codec_get_mclk() / 1000;
2343 codec->bias_level = SND_SOC_BIAS_OFF;
2344
2345 ret = snd_soc_register_codec(codec);
2346 if (ret != 0) {
2347 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
2348 goto error_codec;
2349 }
2350
2351 ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
2352 if (ret != 0) {
2353 dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
2354 snd_soc_unregister_codec(codec);
2355 goto error_codec;
2356 }
2357
2358 return 0;
2359
2360error_codec:
2361 twl4030_codec_enable(codec, 0);
2362 kfree(codec->reg_cache);
2363error_cache:
2364 kfree(twl4030);
2365 return ret;
2366} 2289}
2367 2290
2368static int __devexit twl4030_codec_remove(struct platform_device *pdev) 2291static int __devexit twl4030_codec_remove(struct platform_device *pdev)
2369{ 2292{
2370 struct twl4030_priv *twl4030 = platform_get_drvdata(pdev); 2293 struct twl4030_priv *twl4030 = dev_get_drvdata(&pdev->dev);
2371 2294
2372 snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); 2295 snd_soc_unregister_codec(&pdev->dev);
2373 snd_soc_unregister_codec(&twl4030->codec);
2374 kfree(twl4030->codec.reg_cache);
2375 kfree(twl4030); 2296 kfree(twl4030);
2376
2377 twl4030_codec = NULL;
2378 return 0; 2297 return 0;
2379} 2298}
2380 2299
2381MODULE_ALIAS("platform:twl4030_codec_audio"); 2300MODULE_ALIAS("platform:twl4030-codec");
2382 2301
2383static struct platform_driver twl4030_codec_driver = { 2302static struct platform_driver twl4030_codec_driver = {
2384 .probe = twl4030_codec_probe, 2303 .probe = twl4030_codec_probe,
2385 .remove = __devexit_p(twl4030_codec_remove), 2304 .remove = __devexit_p(twl4030_codec_remove),
2386 .driver = { 2305 .driver = {
2387 .name = "twl4030_codec_audio", 2306 .name = "twl4030-codec",
2388 .owner = THIS_MODULE, 2307 .owner = THIS_MODULE,
2389 }, 2308 },
2390}; 2309};
@@ -2401,14 +2320,6 @@ static void __exit twl4030_exit(void)
2401} 2320}
2402module_exit(twl4030_exit); 2321module_exit(twl4030_exit);
2403 2322
2404struct snd_soc_codec_device soc_codec_dev_twl4030 = {
2405 .probe = twl4030_soc_probe,
2406 .remove = twl4030_soc_remove,
2407 .suspend = twl4030_soc_suspend,
2408 .resume = twl4030_soc_resume,
2409};
2410EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
2411
2412MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); 2323MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
2413MODULE_AUTHOR("Steve Sakoman"); 2324MODULE_AUTHOR("Steve Sakoman");
2414MODULE_LICENSE("GPL"); 2325MODULE_LICENSE("GPL");