diff options
author | Peter Ujfalusi <peter.ujfalusi@nokia.com> | 2009-10-22 06:26:48 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2009-10-25 13:15:10 -0400 |
commit | 7a1fecf57f435e50ed86851cbb701f4b28e65135 (patch) | |
tree | bf992657b7f8307be6cae8a63a4b5afd299db884 /sound/soc/codecs/twl4030.c | |
parent | 1f0f9b67f98a873fca8288ccb7f2a0f3c8f34371 (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.c | 203 |
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 */ |
122 | struct twl4030_priv { | 122 | struct 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, | |||
183 | static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable) | 185 | static 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) | |||
219 | static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute) | 222 | static 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 | }; |
2124 | EXPORT_SYMBOL_GPL(twl4030_dai); | 2125 | EXPORT_SYMBOL_GPL(twl4030_dai); |
2125 | 2126 | ||
2126 | static int twl4030_suspend(struct platform_device *pdev, pm_message_t state) | 2127 | static 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 | ||
2136 | static int twl4030_resume(struct platform_device *pdev) | 2137 | static 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 | /* | 2147 | static struct snd_soc_codec *twl4030_codec; |
2147 | * initialize the driver | ||
2148 | * register the mixer and dsp interfaces with the kernel | ||
2149 | */ | ||
2150 | 2148 | ||
2151 | static int twl4030_init(struct snd_soc_device *socdev) | 2149 | static 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 | ||
2214 | card_err: | 2199 | card_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); |
2217 | pcm_err: | 2202 | |
2218 | kfree(codec->reg_cache); | ||
2219 | return ret; | 2203 | return ret; |
2220 | } | 2204 | } |
2221 | 2205 | ||
2222 | static struct snd_soc_device *twl4030_socdev; | 2206 | static int twl4030_soc_remove(struct platform_device *pdev) |
2223 | |||
2224 | static 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 | |||
2220 | static 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 | |||
2287 | error_codec: | ||
2288 | twl4030_power_down(codec); | ||
2289 | kfree(codec->reg_cache); | ||
2290 | error_cache: | ||
2291 | kfree(twl4030); | ||
2292 | return ret; | ||
2250 | } | 2293 | } |
2251 | 2294 | ||
2252 | static int twl4030_remove(struct platform_device *pdev) | 2295 | static 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 | ||
2267 | struct snd_soc_codec_device soc_codec_dev_twl4030 = { | 2305 | MODULE_ALIAS("platform:twl4030_codec_audio"); |
2268 | .probe = twl4030_probe, | 2306 | |
2269 | .remove = twl4030_remove, | 2307 | static 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 | }; |
2273 | EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | ||
2274 | 2315 | ||
2275 | static int __init twl4030_modinit(void) | 2316 | static 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 | } |
2279 | module_init(twl4030_modinit); | 2320 | module_init(twl4030_modinit); |
2280 | 2321 | ||
2281 | static void __exit twl4030_exit(void) | 2322 | static 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 | } |
2285 | module_exit(twl4030_exit); | 2326 | module_exit(twl4030_exit); |
2286 | 2327 | ||
2328 | struct 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 | }; | ||
2334 | EXPORT_SYMBOL_GPL(soc_codec_dev_twl4030); | ||
2335 | |||
2287 | MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); | 2336 | MODULE_DESCRIPTION("ASoC TWL4030 codec driver"); |
2288 | MODULE_AUTHOR("Steve Sakoman"); | 2337 | MODULE_AUTHOR("Steve Sakoman"); |
2289 | MODULE_LICENSE("GPL"); | 2338 | MODULE_LICENSE("GPL"); |