~ubuntu-branches/ubuntu/precise/alsa-driver/precise

« back to all changes in this revision

Viewing changes to alsa-kernel/soc/samsung/s3c24xx_simtec.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich
  • Date: 2011-02-21 18:06:40 UTC
  • mfrom: (1.1.15 upstream)
  • Revision ID: james.westby@ubuntu.com-20110221180640-a8p2yxtvgf7xbxub
Tags: 1.0.24+dfsg-0ubuntu1
* New upstream release
* Refreshed patches:
  - distinguish_kernel_makefile_and_source_dirs.patch
  - debian_dfsg_configure.patch
* debian/control: Update Vcs-bzr field to point to new branch location

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* sound/soc/samsung/s3c24xx_simtec.c
 
2
 *
 
3
 * Copyright 2009 Simtec Electronics
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License version 2 as
 
7
 * published by the Free Software Foundation.
 
8
*/
 
9
 
 
10
#include <linux/gpio.h>
 
11
#include <linux/clk.h>
 
12
 
 
13
#include <sound/soc.h>
 
14
 
 
15
#include <plat/audio-simtec.h>
 
16
 
 
17
#include "s3c24xx-i2s.h"
 
18
#include "s3c24xx_simtec.h"
 
19
 
 
20
static struct s3c24xx_audio_simtec_pdata *pdata;
 
21
static struct clk *xtal_clk;
 
22
 
 
23
static int spk_gain;
 
24
static int spk_unmute;
 
25
 
 
26
/**
 
27
 * speaker_gain_get - read the speaker gain setting.
 
28
 * @kcontrol: The control for the speaker gain.
 
29
 * @ucontrol: The value that needs to be updated.
 
30
 *
 
31
 * Read the value for the AMP gain control.
 
32
 */
 
33
static int speaker_gain_get(struct snd_kcontrol *kcontrol,
 
34
                            struct snd_ctl_elem_value *ucontrol)
 
35
{
 
36
        ucontrol->value.integer.value[0] = spk_gain;
 
37
        return 0;
 
38
}
 
39
 
 
40
/**
 
41
 * speaker_gain_set - set the value of the speaker amp gain
 
42
 * @value: The value to write.
 
43
 */
 
44
static void speaker_gain_set(int value)
 
45
{
 
46
        gpio_set_value_cansleep(pdata->amp_gain[0], value & 1);
 
47
        gpio_set_value_cansleep(pdata->amp_gain[1], value >> 1);
 
48
}
 
49
 
 
50
/**
 
51
 * speaker_gain_put - set the speaker gain setting.
 
52
 * @kcontrol: The control for the speaker gain.
 
53
 * @ucontrol: The value that needs to be set.
 
54
 *
 
55
 * Set the value of the speaker gain from the specified
 
56
 * @ucontrol setting.
 
57
 *
 
58
 * Note, if the speaker amp is muted, then we do not set a gain value
 
59
 * as at-least one of the ICs that is fitted will try and power up even
 
60
 * if the main control is set to off.
 
61
 */
 
62
static int speaker_gain_put(struct snd_kcontrol *kcontrol,
 
63
                            struct snd_ctl_elem_value *ucontrol)
 
64
{
 
65
        int value = ucontrol->value.integer.value[0];
 
66
 
 
67
        spk_gain = value;
 
68
 
 
69
        if (!spk_unmute)
 
70
                speaker_gain_set(value);
 
71
 
 
72
        return 0;
 
73
}
 
74
 
 
75
static const struct snd_kcontrol_new amp_gain_controls[] = {
 
76
        SOC_SINGLE_EXT("Speaker Gain", 0, 0, 3, 0,
 
77
                       speaker_gain_get, speaker_gain_put),
 
78
};
 
79
 
 
80
/**
 
81
 * spk_unmute_state - set the unmute state of the speaker
 
82
 * @to: zero to unmute, non-zero to ununmute.
 
83
 */
 
84
static void spk_unmute_state(int to)
 
85
{
 
86
        pr_debug("%s: to=%d\n", __func__, to);
 
87
 
 
88
        spk_unmute = to;
 
89
        gpio_set_value(pdata->amp_gpio, to);
 
90
 
 
91
        /* if we're umuting, also re-set the gain */
 
92
        if (to && pdata->amp_gain[0] > 0)
 
93
                speaker_gain_set(spk_gain);
 
94
}
 
95
 
 
96
/**
 
97
 * speaker_unmute_get - read the speaker unmute setting.
 
98
 * @kcontrol: The control for the speaker gain.
 
99
 * @ucontrol: The value that needs to be updated.
 
100
 *
 
101
 * Read the value for the AMP gain control.
 
102
 */
 
103
static int speaker_unmute_get(struct snd_kcontrol *kcontrol,
 
104
                            struct snd_ctl_elem_value *ucontrol)
 
105
{
 
106
        ucontrol->value.integer.value[0] = spk_unmute;
 
107
        return 0;
 
108
}
 
109
 
 
110
/**
 
111
 * speaker_unmute_put - set the speaker unmute setting.
 
112
 * @kcontrol: The control for the speaker gain.
 
113
 * @ucontrol: The value that needs to be set.
 
114
 *
 
115
 * Set the value of the speaker gain from the specified
 
116
 * @ucontrol setting.
 
117
 */
 
118
static int speaker_unmute_put(struct snd_kcontrol *kcontrol,
 
119
                            struct snd_ctl_elem_value *ucontrol)
 
120
{
 
121
        spk_unmute_state(ucontrol->value.integer.value[0]);
 
122
        return 0;
 
123
}
 
124
 
 
125
/* This is added as a manual control as the speaker amps create clicks
 
126
 * when their power state is changed, which are far more noticeable than
 
127
 * anything produced by the CODEC itself.
 
128
 */
 
129
static const struct snd_kcontrol_new amp_unmute_controls[] = {
 
130
        SOC_SINGLE_EXT("Speaker Switch", 0, 0, 1, 0,
 
131
                       speaker_unmute_get, speaker_unmute_put),
 
132
};
 
133
 
 
134
void simtec_audio_init(struct snd_soc_pcm_runtime *rtd)
 
135
{
 
136
        struct snd_soc_codec *codec = rtd->codec;
 
137
 
 
138
        if (pdata->amp_gpio > 0) {
 
139
                pr_debug("%s: adding amp routes\n", __func__);
 
140
 
 
141
                snd_soc_add_controls(codec, amp_unmute_controls,
 
142
                                     ARRAY_SIZE(amp_unmute_controls));
 
143
        }
 
144
 
 
145
        if (pdata->amp_gain[0] > 0) {
 
146
                pr_debug("%s: adding amp controls\n", __func__);
 
147
                snd_soc_add_controls(codec, amp_gain_controls,
 
148
                                     ARRAY_SIZE(amp_gain_controls));
 
149
        }
 
150
}
 
151
EXPORT_SYMBOL_GPL(simtec_audio_init);
 
152
 
 
153
#define CODEC_CLOCK 12000000
 
154
 
 
155
/**
 
156
 * simtec_hw_params - update hardware parameters
 
157
 * @substream: The audio substream instance.
 
158
 * @params: The parameters requested.
 
159
 *
 
160
 * Update the codec data routing and configuration  settings
 
161
 * from the supplied data.
 
162
 */
 
163
static int simtec_hw_params(struct snd_pcm_substream *substream,
 
164
                            struct snd_pcm_hw_params *params)
 
165
{
 
166
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
167
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
 
168
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
 
169
        int ret;
 
170
 
 
171
        /* Set the CODEC as the bus clock master, I2S */
 
172
        ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
 
173
                                  SND_SOC_DAIFMT_NB_NF |
 
174
                                  SND_SOC_DAIFMT_CBM_CFM);
 
175
        if (ret) {
 
176
                pr_err("%s: failed set cpu dai format\n", __func__);
 
177
                return ret;
 
178
        }
 
179
 
 
180
        /* Set the CODEC as the bus clock master */
 
181
        ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
 
182
                                  SND_SOC_DAIFMT_NB_NF |
 
183
                                  SND_SOC_DAIFMT_CBM_CFM);
 
184
        if (ret) {
 
185
                pr_err("%s: failed set codec dai format\n", __func__);
 
186
                return ret;
 
187
        }
 
188
 
 
189
        ret = snd_soc_dai_set_sysclk(codec_dai, 0,
 
190
                                     CODEC_CLOCK, SND_SOC_CLOCK_IN);
 
191
        if (ret) {
 
192
                pr_err( "%s: failed setting codec sysclk\n", __func__);
 
193
                return ret;
 
194
        }
 
195
 
 
196
        if (pdata->use_mpllin) {
 
197
                ret = snd_soc_dai_set_sysclk(cpu_dai, S3C24XX_CLKSRC_MPLL,
 
198
                                             0, SND_SOC_CLOCK_OUT);
 
199
 
 
200
                if (ret) {
 
201
                        pr_err("%s: failed to set MPLLin as clksrc\n",
 
202
                               __func__);
 
203
                        return ret;
 
204
                }
 
205
        }
 
206
 
 
207
        if (pdata->output_cdclk) {
 
208
                int cdclk_scale;
 
209
 
 
210
                cdclk_scale = clk_get_rate(xtal_clk) / CODEC_CLOCK;
 
211
                cdclk_scale--;
 
212
 
 
213
                ret = snd_soc_dai_set_clkdiv(cpu_dai, S3C24XX_DIV_PRESCALER,
 
214
                                             cdclk_scale);
 
215
        }
 
216
 
 
217
        return 0;
 
218
}
 
219
 
 
220
static int simtec_call_startup(struct s3c24xx_audio_simtec_pdata *pd)
 
221
{
 
222
        /* call any board supplied startup code, this currently only
 
223
         * covers the bast/vr1000 which have a CPLD in the way of the
 
224
         * LRCLK */
 
225
        if (pd->startup)
 
226
                pd->startup();
 
227
 
 
228
        return 0;
 
229
}
 
230
 
 
231
static struct snd_soc_ops simtec_snd_ops = {
 
232
        .hw_params      = simtec_hw_params,
 
233
};
 
234
 
 
235
/**
 
236
 * attach_gpio_amp - get and configure the necessary gpios
 
237
 * @dev: The device we're probing.
 
238
 * @pd: The platform data supplied by the board.
 
239
 *
 
240
 * If there is a GPIO based amplifier attached to the board, claim
 
241
 * the necessary GPIO lines for it, and set default values.
 
242
 */
 
243
static int attach_gpio_amp(struct device *dev,
 
244
                           struct s3c24xx_audio_simtec_pdata *pd)
 
245
{
 
246
        int ret;
 
247
 
 
248
        /* attach gpio amp gain (if any) */
 
249
        if (pdata->amp_gain[0] > 0) {
 
250
                ret = gpio_request(pd->amp_gain[0], "gpio-amp-gain0");
 
251
                if (ret) {
 
252
                        dev_err(dev, "cannot get amp gpio gain0\n");
 
253
                        return ret;
 
254
                }
 
255
 
 
256
                ret = gpio_request(pd->amp_gain[1], "gpio-amp-gain1");
 
257
                if (ret) {
 
258
                        dev_err(dev, "cannot get amp gpio gain1\n");
 
259
                        gpio_free(pdata->amp_gain[0]);
 
260
                        return ret;
 
261
                }
 
262
 
 
263
                gpio_direction_output(pd->amp_gain[0], 0);
 
264
                gpio_direction_output(pd->amp_gain[1], 0);
 
265
        }
 
266
 
 
267
        /* note, currently we assume GPA0 isn't valid amp */
 
268
        if (pdata->amp_gpio > 0) {
 
269
                ret = gpio_request(pd->amp_gpio, "gpio-amp");
 
270
                if (ret) {
 
271
                        dev_err(dev, "cannot get amp gpio %d (%d)\n",
 
272
                                pd->amp_gpio, ret);
 
273
                        goto err_amp;
 
274
                }
 
275
 
 
276
                /* set the amp off at startup */
 
277
                spk_unmute_state(0);
 
278
        }
 
279
 
 
280
        return 0;
 
281
 
 
282
err_amp:
 
283
        if (pd->amp_gain[0] > 0) {
 
284
                gpio_free(pd->amp_gain[0]);
 
285
                gpio_free(pd->amp_gain[1]);
 
286
        }
 
287
 
 
288
        return ret;
 
289
}
 
290
 
 
291
static void detach_gpio_amp(struct s3c24xx_audio_simtec_pdata *pd)
 
292
{
 
293
        if (pd->amp_gain[0] > 0) {
 
294
                gpio_free(pd->amp_gain[0]);
 
295
                gpio_free(pd->amp_gain[1]);
 
296
        }
 
297
 
 
298
        if (pd->amp_gpio > 0)
 
299
                gpio_free(pd->amp_gpio);
 
300
}
 
301
 
 
302
#ifdef CONFIG_PM
 
303
int simtec_audio_resume(struct device *dev)
 
304
{
 
305
        simtec_call_startup(pdata);
 
306
        return 0;
 
307
}
 
308
 
 
309
const struct dev_pm_ops simtec_audio_pmops = {
 
310
        .resume = simtec_audio_resume,
 
311
};
 
312
EXPORT_SYMBOL_GPL(simtec_audio_pmops);
 
313
#endif
 
314
 
 
315
int __devinit simtec_audio_core_probe(struct platform_device *pdev,
 
316
                                      struct snd_soc_card *card)
 
317
{
 
318
        struct platform_device *snd_dev;
 
319
        int ret;
 
320
 
 
321
        card->dai_link->ops = &simtec_snd_ops;
 
322
 
 
323
        pdata = pdev->dev.platform_data;
 
324
        if (!pdata) {
 
325
                dev_err(&pdev->dev, "no platform data supplied\n");
 
326
                return -EINVAL;
 
327
        }
 
328
 
 
329
        simtec_call_startup(pdata);
 
330
 
 
331
        xtal_clk = clk_get(&pdev->dev, "xtal");
 
332
        if (IS_ERR(xtal_clk)) {
 
333
                dev_err(&pdev->dev, "could not get clkout0\n");
 
334
                return -EINVAL;
 
335
        }
 
336
 
 
337
        dev_info(&pdev->dev, "xtal rate is %ld\n", clk_get_rate(xtal_clk));
 
338
 
 
339
        ret = attach_gpio_amp(&pdev->dev, pdata);
 
340
        if (ret)
 
341
                goto err_clk;
 
342
 
 
343
        snd_dev = platform_device_alloc("soc-audio", -1);
 
344
        if (!snd_dev) {
 
345
                dev_err(&pdev->dev, "failed to alloc soc-audio devicec\n");
 
346
                ret = -ENOMEM;
 
347
                goto err_gpio;
 
348
        }
 
349
 
 
350
        platform_set_drvdata(snd_dev, card);
 
351
 
 
352
        ret = platform_device_add(snd_dev);
 
353
        if (ret) {
 
354
                dev_err(&pdev->dev, "failed to add soc-audio dev\n");
 
355
                goto err_pdev;
 
356
        }
 
357
 
 
358
        platform_set_drvdata(pdev, snd_dev);
 
359
        return 0;
 
360
 
 
361
err_pdev:
 
362
        platform_device_put(snd_dev);
 
363
 
 
364
err_gpio:
 
365
        detach_gpio_amp(pdata);
 
366
 
 
367
err_clk:
 
368
        clk_put(xtal_clk);
 
369
        return ret;
 
370
}
 
371
EXPORT_SYMBOL_GPL(simtec_audio_core_probe);
 
372
 
 
373
int __devexit simtec_audio_remove(struct platform_device *pdev)
 
374
{
 
375
        struct platform_device *snd_dev = platform_get_drvdata(pdev);
 
376
 
 
377
        platform_device_unregister(snd_dev);
 
378
 
 
379
        detach_gpio_amp(pdata);
 
380
        clk_put(xtal_clk);
 
381
        return 0;
 
382
}
 
383
EXPORT_SYMBOL_GPL(simtec_audio_remove);
 
384
 
 
385
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
 
386
MODULE_DESCRIPTION("ALSA SoC Simtec Audio common support");
 
387
MODULE_LICENSE("GPL");