aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/twl4030.c
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@nokia.com>2009-10-22 06:26:48 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2009-10-25 13:15:10 -0400
commit7a1fecf57f435e50ed86851cbb701f4b28e65135 (patch)
treebf992657b7f8307be6cae8a63a4b5afd299db884 /sound/soc/codecs/twl4030.c
parent1f0f9b67f98a873fca8288ccb7f2a0f3c8f34371 (diff)
ASoC: TWL4030: Driver registration via twl4030_codec MFD
Change the way how the twl4030 soc codec driver is loaded/probed. Use the device probing via tlw4030_codec MFD device. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@nokia.com> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/twl4030.c')
-rw-r--r--sound/soc/codecs/twl4030.c203
1 files changed, 126 insertions, 77 deletions
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 559e9b279289..5c5a4c0a424f 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -120,6 +120,8 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
120 120
121/* codec private data */ 121/* codec private data */
122struct twl4030_priv { 122struct twl4030_priv {
123 struct snd_soc_codec codec;
124
123 unsigned int bypass_state; 125 unsigned int bypass_state;
124 unsigned int codec_powered; 126 unsigned int codec_powered;
125 unsigned int codec_muted; 127 unsigned int codec_muted;
@@ -183,19 +185,20 @@ static int twl4030_write(struct snd_soc_codec *codec,
183static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) 185static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
184{ 186{
185 struct twl4030_priv *twl4030 = codec->private_data; 187 struct twl4030_priv *twl4030 = codec->private_data;
186 u8 mode; 188 int mode;
187 189
188 if (enable == twl4030->codec_powered) 190 if (enable == twl4030->codec_powered)
189 return; 191 return;
190 192
191 mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
192 if (enable) 193 if (enable)
193 mode |= TWL4030_CODECPDZ; 194 mode = twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);
194 else 195 else
195 mode &= ~TWL4030_CODECPDZ; 196 mode = twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);
196 197
197 twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode); 198 if (mode >= 0) {
198 twl4030->codec_powered = enable; 199 twl4030_write_reg_cache(codec, TWL4030_REG_CODEC_MODE, mode);
200 twl4030->codec_powered = enable;
201 }
199 202
200 /* REVISIT: this delay is present in TI sample drivers */ 203 /* REVISIT: this delay is present in TI sample drivers */
201 /* but there seems to be no TRM requirement for it */ 204 /* but there seems to be no TRM requirement for it */
@@ -219,22 +222,20 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
219static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) 222static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
220{ 223{
221 struct twl4030_priv *twl4030 = codec->private_data; 224 struct twl4030_priv *twl4030 = codec->private_data;
222 u8 reg_val; 225 int status;
223 226
224 if (mute == twl4030->codec_muted) 227 if (mute == twl4030->codec_muted)
225 return; 228 return;
226 229
227 if (mute) { 230 if (mute)
228 /* Disable PLL */ 231 /* Disable PLL */
229 reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); 232 status = twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);
230 reg_val &= ~TWL4030_APLL_EN; 233 else
231 twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
232 } else {
233 /* Enable PLL */ 234 /* Enable PLL */
234 reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL); 235 status = twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);
235 reg_val |= TWL4030_APLL_EN; 236
236 twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val); 237 if (status >= 0)
237 } 238 twl4030_write_reg_cache(codec, TWL4030_REG_APLL_CTL, status);
238 239
239 twl4030->codec_muted = mute; 240 twl4030->codec_muted = mute;
240} 241}
@@ -2123,7 +2124,7 @@ struct snd_soc_dai twl4030_dai[] = {
2123}; 2124};
2124EXPORT_SYMBOL_GPL(twl4030_dai); 2125EXPORT_SYMBOL_GPL(twl4030_dai);
2125 2126
2126static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) 2127static int twl4030_soc_suspend(struct platform_device *pdev, pm_message_t state)
2127{ 2128{
2128 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 2129 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2129 struct snd_soc_codec *codec = socdev->card->codec; 2130 struct snd_soc_codec *codec = socdev->card->codec;
@@ -2133,7 +2134,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
2133 return 0; 2134 return 0;
2134} 2135}
2135 2136
2136static int twl4030_resume(struct platform_device *pdev) 2137static int twl4030_soc_resume(struct platform_device *pdev)
2137{ 2138{
2138 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 2139 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2139 struct snd_soc_codec *codec = socdev->card->codec; 2140 struct snd_soc_codec *codec = socdev->card->codec;
@@ -2143,32 +2144,21 @@ static int twl4030_resume(struct platform_device *pdev)
2143 return 0; 2144 return 0;
2144} 2145}
2145 2146
2146/* 2147static struct snd_soc_codec *twl4030_codec;
2147 * initialize the driver
2148 * register the mixer and dsp interfaces with the kernel
2149 */
2150 2148
2151static int twl4030_init(struct snd_soc_device *socdev) 2149static int twl4030_soc_probe(struct platform_device *pdev)
2152{ 2150{
2153 struct snd_soc_codec *codec = socdev->card->codec; 2151 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2154 struct twl4030_setup_data *setup = socdev->codec_data; 2152 struct twl4030_setup_data *setup = socdev->codec_data;
2155 struct twl4030_priv *twl4030 = codec->private_data; 2153 struct snd_soc_codec *codec;
2156 int ret = 0; 2154 struct twl4030_priv *twl4030;
2155 int ret;
2157 2156
2158 printk(KERN_INFO "TWL4030 Audio Codec init \n"); 2157 BUG_ON(!twl4030_codec);
2159 2158
2160 codec->name = "twl4030"; 2159 codec = twl4030_codec;
2161 codec->owner = THIS_MODULE; 2160 twl4030 = codec->private_data;
2162 codec->read = twl4030_read_reg_cache; 2161 socdev->card->codec = codec;
2163 codec->write = twl4030_write;
2164 codec->set_bias_level = twl4030_set_bias_level;
2165 codec->dai = twl4030_dai;
2166 codec->num_dai = ARRAY_SIZE(twl4030_dai),
2167 codec->reg_cache_size = sizeof(twl4030_reg);
2168 codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
2169 GFP_KERNEL);
2170 if (codec->reg_cache == NULL)
2171 return -ENOMEM;
2172 2162
2173 /* Configuration for headset ramp delay from setup data */ 2163 /* Configuration for headset ramp delay from setup data */
2174 if (setup) { 2164 if (setup) {
@@ -2190,100 +2180,159 @@ static int twl4030_init(struct snd_soc_device *socdev)
2190 /* register pcms */ 2180 /* register pcms */
2191 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); 2181 ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
2192 if (ret < 0) { 2182 if (ret < 0) {
2193 printk(KERN_ERR "twl4030: failed to create pcms\n"); 2183 dev_err(&pdev->dev, "failed to create pcms\n");
2194 goto pcm_err; 2184 return ret;
2195 } 2185 }
2196 2186
2197 twl4030_init_chip(codec);
2198
2199 /* power on device */
2200 twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
2201
2202 snd_soc_add_controls(codec, twl4030_snd_controls, 2187 snd_soc_add_controls(codec, twl4030_snd_controls,
2203 ARRAY_SIZE(twl4030_snd_controls)); 2188 ARRAY_SIZE(twl4030_snd_controls));
2204 twl4030_add_widgets(codec); 2189 twl4030_add_widgets(codec);
2205 2190
2206 ret = snd_soc_init_card(socdev); 2191 ret = snd_soc_init_card(socdev);
2207 if (ret < 0) { 2192 if (ret < 0) {
2208 printk(KERN_ERR "twl4030: failed to register card\n"); 2193 dev_err(&pdev->dev, "failed to register card\n");
2209 goto card_err; 2194 goto card_err;
2210 } 2195 }
2211 2196
2212 return ret; 2197 return 0;
2213 2198
2214card_err: 2199card_err:
2215 snd_soc_free_pcms(socdev); 2200 snd_soc_free_pcms(socdev);
2216 snd_soc_dapm_free(socdev); 2201 snd_soc_dapm_free(socdev);
2217pcm_err: 2202
2218 kfree(codec->reg_cache);
2219 return ret; 2203 return ret;
2220} 2204}
2221 2205
2222static struct snd_soc_device *twl4030_socdev; 2206static int twl4030_soc_remove(struct platform_device *pdev)
2223
2224static int twl4030_probe(struct platform_device *pdev)
2225{ 2207{
2226 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 2208 struct snd_soc_device *socdev = platform_get_drvdata(pdev);
2209 struct snd_soc_codec *codec = socdev->card->codec;
2210
2211 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
2212 snd_soc_free_pcms(socdev);
2213 snd_soc_dapm_free(socdev);
2214 kfree(codec->private_data);
2215 kfree(codec);
2216
2217 return 0;
2218}
2219
2220static int __devinit twl4030_codec_probe(struct platform_device *pdev)
2221{
2222 struct twl4030_codec_audio_data *pdata = pdev->dev.platform_data;
2227 struct snd_soc_codec *codec; 2223 struct snd_soc_codec *codec;
2228 struct twl4030_priv *twl4030; 2224 struct twl4030_priv *twl4030;
2225 int ret;
2229 2226
2230 codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); 2227 if (!pdata || !(pdata->audio_mclk == 19200000 ||
2231 if (codec == NULL) 2228 pdata->audio_mclk == 26000000 ||
2232 return -ENOMEM; 2229 pdata->audio_mclk == 38400000)) {
2230 dev_err(&pdev->dev, "Invalid platform_data\n");
2231 return -EINVAL;
2232 }
2233 2233
2234 twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL); 2234 twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
2235 if (twl4030 == NULL) { 2235 if (twl4030 == NULL) {
2236 kfree(codec); 2236 dev_err(&pdev->dev, "Can not allocate memroy\n");
2237 return -ENOMEM; 2237 return -ENOMEM;
2238 } 2238 }
2239 2239
2240 codec = &twl4030->codec;
2240 codec->private_data = twl4030; 2241 codec->private_data = twl4030;
2241 socdev->card->codec = codec; 2242 codec->dev = &pdev->dev;
2243 twl4030_dai[0].dev = &pdev->dev;
2244 twl4030_dai[1].dev = &pdev->dev;
2245
2242 mutex_init(&codec->mutex); 2246 mutex_init(&codec->mutex);
2243 INIT_LIST_HEAD(&codec->dapm_widgets); 2247 INIT_LIST_HEAD(&codec->dapm_widgets);
2244 INIT_LIST_HEAD(&codec->dapm_paths); 2248 INIT_LIST_HEAD(&codec->dapm_paths);
2245 2249
2246 twl4030_socdev = socdev; 2250 codec->name = "twl4030";
2247 twl4030_init(socdev); 2251 codec->owner = THIS_MODULE;
2252 codec->read = twl4030_read_reg_cache;
2253 codec->write = twl4030_write;
2254 codec->set_bias_level = twl4030_set_bias_level;
2255 codec->dai = twl4030_dai;
2256 codec->num_dai = ARRAY_SIZE(twl4030_dai),
2257 codec->reg_cache_size = sizeof(twl4030_reg);
2258 codec->reg_cache = kmemdup(twl4030_reg, sizeof(twl4030_reg),
2259 GFP_KERNEL);
2260 if (codec->reg_cache == NULL) {
2261 ret = -ENOMEM;
2262 goto error_cache;
2263 }
2264
2265 platform_set_drvdata(pdev, twl4030);
2266 twl4030_codec = codec;
2267
2268 /* Set the defaults, and power up the codec */
2269 twl4030_init_chip(codec);
2270 twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
2271
2272 ret = snd_soc_register_codec(codec);
2273 if (ret != 0) {
2274 dev_err(codec->dev, "Failed to register codec: %d\n", ret);
2275 goto error_codec;
2276 }
2277
2278 ret = snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai));
2279 if (ret != 0) {
2280 dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
2281 snd_soc_unregister_codec(codec);
2282 goto error_codec;
2283 }
2248 2284
2249 return 0; 2285 return 0;
2286
2287error_codec:
2288 twl4030_power_down(codec);
2289 kfree(codec->reg_cache);
2290error_cache:
2291 kfree(twl4030);
2292 return ret;
2250} 2293}
2251 2294
2252static int twl4030_remove(struct platform_device *pdev) 2295static int __devexit twl4030_codec_remove(struct platform_device *pdev)
2253{ 2296{
2254 struct snd_soc_device *socdev = platform_get_drvdata(pdev); 2297 struct twl4030_priv *twl4030 = platform_get_drvdata(pdev);
2255 struct snd_soc_codec *codec = socdev->card->codec;
2256 2298
2257 printk(KERN_INFO "TWL4030 Audio Codec remove\n"); 2299 kfree(twl4030);
2258 twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
2259 snd_soc_free_pcms(socdev);
2260 snd_soc_dapm_free(socdev);
2261 kfree(codec->private_data);
2262 kfree(codec);
2263 2300
2301 twl4030_codec = NULL;
2264 return 0; 2302 return 0;
2265} 2303}
2266 2304
2267struct snd_soc_codec_device soc_codec_dev_twl4030 = { 2305MODULE_ALIAS("platform:twl4030_codec_audio");
2268 .probe = twl4030_probe, 2306
2269 .remove = twl4030_remove, 2307static struct platform_driver twl4030_codec_driver = {
2270 .suspend = twl4030_suspend, 2308 .probe = twl4030_codec_probe,
2271 .resume = twl4030_resume, 2309 .remove = __devexit_p(twl4030_codec_remove),
2310 .driver = {
2311 .name = "twl4030_codec_audio",
2312 .owner = THIS_MODULE,
2313 },
2272}; 2314};
2273EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
2274 2315
2275static int __init twl4030_modinit(void) 2316static int __init twl4030_modinit(void)
2276{ 2317{
2277 return snd_soc_register_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); 2318 return platform_driver_register(&twl4030_codec_driver);
2278} 2319}
2279module_init(twl4030_modinit); 2320module_init(twl4030_modinit);
2280 2321
2281static void __exit twl4030_exit(void) 2322static void __exit twl4030_exit(void)
2282{ 2323{
2283 snd_soc_unregister_dais(&twl4030_dai[0], ARRAY_SIZE(twl4030_dai)); 2324 platform_driver_unregister(&twl4030_codec_driver);
2284} 2325}
2285module_exit(twl4030_exit); 2326module_exit(twl4030_exit);
2286 2327
2328struct snd_soc_codec_device soc_codec_dev_twl4030 = {
2329 .probe = twl4030_soc_probe,
2330 .remove = twl4030_soc_remove,
2331 .suspend = twl4030_soc_suspend,
2332 .resume = twl4030_soc_resume,
2333};
2334EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030);
2335
2287MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); 2336MODULE_DESCRIPTION("ASoC TWL4030 codec driver");
2288MODULE_AUTHOR("Steve Sakoman"); 2337MODULE_AUTHOR("Steve Sakoman");
2289MODULE_LICENSE("GPL"); 2338MODULE_LICENSE("GPL");