12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23fabe089SMatti J. Aaltonen /*
33fabe089SMatti J. Aaltonen * ALSA SoC WL1273 codec driver
43fabe089SMatti J. Aaltonen *
53fabe089SMatti J. Aaltonen * Author: Matti Aaltonen, <matti.j.aaltonen@nokia.com>
63fabe089SMatti J. Aaltonen *
740285f83SMatti Aaltonen * Copyright: (C) 2010, 2011 Nokia Corporation
83fabe089SMatti J. Aaltonen */
93fabe089SMatti J. Aaltonen
103fabe089SMatti J. Aaltonen #include <linux/mfd/wl1273-core.h>
113fabe089SMatti J. Aaltonen #include <linux/slab.h>
12da155d5bSPaul Gortmaker #include <linux/module.h>
133fabe089SMatti J. Aaltonen #include <sound/pcm.h>
143fabe089SMatti J. Aaltonen #include <sound/pcm_params.h>
150d911baeSJarkko Nikula #include <sound/soc.h>
163fabe089SMatti J. Aaltonen #include <sound/initval.h>
173fabe089SMatti J. Aaltonen
183fabe089SMatti J. Aaltonen #include "wl1273.h"
193fabe089SMatti J. Aaltonen
203fabe089SMatti J. Aaltonen enum wl1273_mode { WL1273_MODE_BT, WL1273_MODE_FM_RX, WL1273_MODE_FM_TX };
213fabe089SMatti J. Aaltonen
223fabe089SMatti J. Aaltonen /* codec private data */
233fabe089SMatti J. Aaltonen struct wl1273_priv {
243fabe089SMatti J. Aaltonen enum wl1273_mode mode;
253fabe089SMatti J. Aaltonen struct wl1273_core *core;
263fabe089SMatti J. Aaltonen unsigned int channels;
273fabe089SMatti J. Aaltonen };
283fabe089SMatti J. Aaltonen
snd_wl1273_fm_set_i2s_mode(struct wl1273_core * core,int rate,int width)293fabe089SMatti J. Aaltonen static int snd_wl1273_fm_set_i2s_mode(struct wl1273_core *core,
303fabe089SMatti J. Aaltonen int rate, int width)
313fabe089SMatti J. Aaltonen {
32228dd545SMatti J. Aaltonen struct device *dev = &core->client->dev;
333fabe089SMatti J. Aaltonen int r = 0;
343fabe089SMatti J. Aaltonen u16 mode;
353fabe089SMatti J. Aaltonen
363fabe089SMatti J. Aaltonen dev_dbg(dev, "rate: %d\n", rate);
373fabe089SMatti J. Aaltonen dev_dbg(dev, "width: %d\n", width);
383fabe089SMatti J. Aaltonen
393fabe089SMatti J. Aaltonen mutex_lock(&core->lock);
403fabe089SMatti J. Aaltonen
413fabe089SMatti J. Aaltonen mode = core->i2s_mode & ~WL1273_IS2_WIDTH & ~WL1273_IS2_RATE;
423fabe089SMatti J. Aaltonen
433fabe089SMatti J. Aaltonen switch (rate) {
443fabe089SMatti J. Aaltonen case 48000:
453fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_48K;
463fabe089SMatti J. Aaltonen break;
473fabe089SMatti J. Aaltonen case 44100:
483fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_44_1K;
493fabe089SMatti J. Aaltonen break;
503fabe089SMatti J. Aaltonen case 32000:
513fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_32K;
523fabe089SMatti J. Aaltonen break;
533fabe089SMatti J. Aaltonen case 22050:
543fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_22_05K;
553fabe089SMatti J. Aaltonen break;
563fabe089SMatti J. Aaltonen case 16000:
573fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_16K;
583fabe089SMatti J. Aaltonen break;
593fabe089SMatti J. Aaltonen case 12000:
603fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_12K;
613fabe089SMatti J. Aaltonen break;
623fabe089SMatti J. Aaltonen case 11025:
633fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_11_025;
643fabe089SMatti J. Aaltonen break;
653fabe089SMatti J. Aaltonen case 8000:
663fabe089SMatti J. Aaltonen mode |= WL1273_IS2_RATE_8K;
673fabe089SMatti J. Aaltonen break;
683fabe089SMatti J. Aaltonen default:
693fabe089SMatti J. Aaltonen dev_err(dev, "Sampling rate: %d not supported\n", rate);
703fabe089SMatti J. Aaltonen r = -EINVAL;
713fabe089SMatti J. Aaltonen goto out;
723fabe089SMatti J. Aaltonen }
733fabe089SMatti J. Aaltonen
743fabe089SMatti J. Aaltonen switch (width) {
753fabe089SMatti J. Aaltonen case 16:
763fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_32;
773fabe089SMatti J. Aaltonen break;
783fabe089SMatti J. Aaltonen case 20:
793fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_40;
803fabe089SMatti J. Aaltonen break;
813fabe089SMatti J. Aaltonen case 24:
823fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_48;
833fabe089SMatti J. Aaltonen break;
843fabe089SMatti J. Aaltonen case 25:
853fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_50;
863fabe089SMatti J. Aaltonen break;
873fabe089SMatti J. Aaltonen case 30:
883fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_60;
893fabe089SMatti J. Aaltonen break;
903fabe089SMatti J. Aaltonen case 32:
913fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_64;
923fabe089SMatti J. Aaltonen break;
933fabe089SMatti J. Aaltonen case 40:
943fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_80;
953fabe089SMatti J. Aaltonen break;
963fabe089SMatti J. Aaltonen case 48:
973fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_96;
983fabe089SMatti J. Aaltonen break;
993fabe089SMatti J. Aaltonen case 64:
1003fabe089SMatti J. Aaltonen mode |= WL1273_IS2_WIDTH_128;
1013fabe089SMatti J. Aaltonen break;
1023fabe089SMatti J. Aaltonen default:
1033fabe089SMatti J. Aaltonen dev_err(dev, "Data width: %d not supported\n", width);
1043fabe089SMatti J. Aaltonen r = -EINVAL;
1053fabe089SMatti J. Aaltonen goto out;
1063fabe089SMatti J. Aaltonen }
1073fabe089SMatti J. Aaltonen
1083fabe089SMatti J. Aaltonen dev_dbg(dev, "WL1273_I2S_DEF_MODE: 0x%04x\n", WL1273_I2S_DEF_MODE);
1093fabe089SMatti J. Aaltonen dev_dbg(dev, "core->i2s_mode: 0x%04x\n", core->i2s_mode);
1103fabe089SMatti J. Aaltonen dev_dbg(dev, "mode: 0x%04x\n", mode);
1113fabe089SMatti J. Aaltonen
1123fabe089SMatti J. Aaltonen if (core->i2s_mode != mode) {
113228dd545SMatti J. Aaltonen r = core->write(core, WL1273_I2S_MODE_CONFIG_SET, mode);
1143fabe089SMatti J. Aaltonen if (r)
1153fabe089SMatti J. Aaltonen goto out;
1163fabe089SMatti J. Aaltonen
1173fabe089SMatti J. Aaltonen core->i2s_mode = mode;
118228dd545SMatti J. Aaltonen r = core->write(core, WL1273_AUDIO_ENABLE,
1193fabe089SMatti J. Aaltonen WL1273_AUDIO_ENABLE_I2S);
1203fabe089SMatti J. Aaltonen if (r)
1213fabe089SMatti J. Aaltonen goto out;
1223fabe089SMatti J. Aaltonen }
1233fabe089SMatti J. Aaltonen out:
1243fabe089SMatti J. Aaltonen mutex_unlock(&core->lock);
1253fabe089SMatti J. Aaltonen
1263fabe089SMatti J. Aaltonen return r;
1273fabe089SMatti J. Aaltonen }
1283fabe089SMatti J. Aaltonen
snd_wl1273_fm_set_channel_number(struct wl1273_core * core,int channel_number)1293fabe089SMatti J. Aaltonen static int snd_wl1273_fm_set_channel_number(struct wl1273_core *core,
1303fabe089SMatti J. Aaltonen int channel_number)
1313fabe089SMatti J. Aaltonen {
132228dd545SMatti J. Aaltonen struct device *dev = &core->client->dev;
1333fabe089SMatti J. Aaltonen int r = 0;
1343fabe089SMatti J. Aaltonen
1353fabe089SMatti J. Aaltonen dev_dbg(dev, "%s\n", __func__);
1363fabe089SMatti J. Aaltonen
1373fabe089SMatti J. Aaltonen mutex_lock(&core->lock);
1383fabe089SMatti J. Aaltonen
1393fabe089SMatti J. Aaltonen if (core->channel_number == channel_number)
1403fabe089SMatti J. Aaltonen goto out;
1413fabe089SMatti J. Aaltonen
1423fabe089SMatti J. Aaltonen if (channel_number == 1 && core->mode == WL1273_MODE_RX)
143228dd545SMatti J. Aaltonen r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_MONO);
1443fabe089SMatti J. Aaltonen else if (channel_number == 1 && core->mode == WL1273_MODE_TX)
145228dd545SMatti J. Aaltonen r = core->write(core, WL1273_MONO_SET, WL1273_TX_MONO);
1463fabe089SMatti J. Aaltonen else if (channel_number == 2 && core->mode == WL1273_MODE_RX)
147228dd545SMatti J. Aaltonen r = core->write(core, WL1273_MOST_MODE_SET, WL1273_RX_STEREO);
1483fabe089SMatti J. Aaltonen else if (channel_number == 2 && core->mode == WL1273_MODE_TX)
149228dd545SMatti J. Aaltonen r = core->write(core, WL1273_MONO_SET, WL1273_TX_STEREO);
1503fabe089SMatti J. Aaltonen else
1513fabe089SMatti J. Aaltonen r = -EINVAL;
1523fabe089SMatti J. Aaltonen out:
1533fabe089SMatti J. Aaltonen mutex_unlock(&core->lock);
1543fabe089SMatti J. Aaltonen
1553fabe089SMatti J. Aaltonen return r;
1563fabe089SMatti J. Aaltonen }
1573fabe089SMatti J. Aaltonen
snd_wl1273_get_audio_route(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1583fabe089SMatti J. Aaltonen static int snd_wl1273_get_audio_route(struct snd_kcontrol *kcontrol,
1593fabe089SMatti J. Aaltonen struct snd_ctl_elem_value *ucontrol)
1603fabe089SMatti J. Aaltonen {
161559ab397SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
162559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
1633fabe089SMatti J. Aaltonen
164154a0fddSTakashi Iwai ucontrol->value.enumerated.item[0] = wl1273->mode;
1653fabe089SMatti J. Aaltonen
1663fabe089SMatti J. Aaltonen return 0;
1673fabe089SMatti J. Aaltonen }
1683fabe089SMatti J. Aaltonen
16940285f83SMatti Aaltonen /*
17040285f83SMatti Aaltonen * TODO: Implement the audio routing in the driver. Now this control
17140285f83SMatti Aaltonen * only indicates the setting that has been done elsewhere (in the user
17240285f83SMatti Aaltonen * space).
17340285f83SMatti Aaltonen */
17440285f83SMatti Aaltonen static const char * const wl1273_audio_route[] = { "Bt", "FmRx", "FmTx" };
1753fabe089SMatti J. Aaltonen
snd_wl1273_set_audio_route(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1763fabe089SMatti J. Aaltonen static int snd_wl1273_set_audio_route(struct snd_kcontrol *kcontrol,
1773fabe089SMatti J. Aaltonen struct snd_ctl_elem_value *ucontrol)
1783fabe089SMatti J. Aaltonen {
179559ab397SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
180559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
1813fabe089SMatti J. Aaltonen
182154a0fddSTakashi Iwai if (wl1273->mode == ucontrol->value.enumerated.item[0])
1832c4ee9b5SMatti J. Aaltonen return 0;
1842c4ee9b5SMatti J. Aaltonen
1853fabe089SMatti J. Aaltonen /* Do not allow changes while stream is running */
1865e518eddSKuninori Morimoto if (snd_soc_component_active(component))
1873fabe089SMatti J. Aaltonen return -EPERM;
1883fabe089SMatti J. Aaltonen
189154a0fddSTakashi Iwai if (ucontrol->value.enumerated.item[0] >= ARRAY_SIZE(wl1273_audio_route))
1903fabe089SMatti J. Aaltonen return -EINVAL;
1913fabe089SMatti J. Aaltonen
192154a0fddSTakashi Iwai wl1273->mode = ucontrol->value.enumerated.item[0];
1933fabe089SMatti J. Aaltonen
1943fabe089SMatti J. Aaltonen return 1;
1953fabe089SMatti J. Aaltonen }
1963fabe089SMatti J. Aaltonen
1974ec20a97STakashi Iwai static SOC_ENUM_SINGLE_EXT_DECL(wl1273_enum, wl1273_audio_route);
1983fabe089SMatti J. Aaltonen
snd_wl1273_fm_audio_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)1993fabe089SMatti J. Aaltonen static int snd_wl1273_fm_audio_get(struct snd_kcontrol *kcontrol,
2003fabe089SMatti J. Aaltonen struct snd_ctl_elem_value *ucontrol)
2013fabe089SMatti J. Aaltonen {
202559ab397SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
203559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
2043fabe089SMatti J. Aaltonen
205559ab397SKuninori Morimoto dev_dbg(component->dev, "%s: enter.\n", __func__);
2063fabe089SMatti J. Aaltonen
207154a0fddSTakashi Iwai ucontrol->value.enumerated.item[0] = wl1273->core->audio_mode;
2083fabe089SMatti J. Aaltonen
2093fabe089SMatti J. Aaltonen return 0;
2103fabe089SMatti J. Aaltonen }
2113fabe089SMatti J. Aaltonen
snd_wl1273_fm_audio_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2123fabe089SMatti J. Aaltonen static int snd_wl1273_fm_audio_put(struct snd_kcontrol *kcontrol,
2133fabe089SMatti J. Aaltonen struct snd_ctl_elem_value *ucontrol)
2143fabe089SMatti J. Aaltonen {
215559ab397SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
216559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
2173fabe089SMatti J. Aaltonen int val, r = 0;
2183fabe089SMatti J. Aaltonen
219559ab397SKuninori Morimoto dev_dbg(component->dev, "%s: enter.\n", __func__);
2203fabe089SMatti J. Aaltonen
221154a0fddSTakashi Iwai val = ucontrol->value.enumerated.item[0];
2223fabe089SMatti J. Aaltonen if (wl1273->core->audio_mode == val)
2233fabe089SMatti J. Aaltonen return 0;
2243fabe089SMatti J. Aaltonen
225228dd545SMatti J. Aaltonen r = wl1273->core->set_audio(wl1273->core, val);
2263fabe089SMatti J. Aaltonen if (r < 0)
2273fabe089SMatti J. Aaltonen return r;
2283fabe089SMatti J. Aaltonen
2293fabe089SMatti J. Aaltonen return 1;
2303fabe089SMatti J. Aaltonen }
2313fabe089SMatti J. Aaltonen
23240285f83SMatti Aaltonen static const char * const wl1273_audio_strings[] = { "Digital", "Analog" };
2333fabe089SMatti J. Aaltonen
2344ec20a97STakashi Iwai static SOC_ENUM_SINGLE_EXT_DECL(wl1273_audio_enum, wl1273_audio_strings);
2353fabe089SMatti J. Aaltonen
snd_wl1273_fm_volume_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2363fabe089SMatti J. Aaltonen static int snd_wl1273_fm_volume_get(struct snd_kcontrol *kcontrol,
2373fabe089SMatti J. Aaltonen struct snd_ctl_elem_value *ucontrol)
2383fabe089SMatti J. Aaltonen {
239559ab397SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
240559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
2413fabe089SMatti J. Aaltonen
242559ab397SKuninori Morimoto dev_dbg(component->dev, "%s: enter.\n", __func__);
2433fabe089SMatti J. Aaltonen
2443fabe089SMatti J. Aaltonen ucontrol->value.integer.value[0] = wl1273->core->volume;
2453fabe089SMatti J. Aaltonen
2463fabe089SMatti J. Aaltonen return 0;
2473fabe089SMatti J. Aaltonen }
2483fabe089SMatti J. Aaltonen
snd_wl1273_fm_volume_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)2493fabe089SMatti J. Aaltonen static int snd_wl1273_fm_volume_put(struct snd_kcontrol *kcontrol,
2503fabe089SMatti J. Aaltonen struct snd_ctl_elem_value *ucontrol)
2513fabe089SMatti J. Aaltonen {
252559ab397SKuninori Morimoto struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
253559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
2543fabe089SMatti J. Aaltonen int r;
2553fabe089SMatti J. Aaltonen
256559ab397SKuninori Morimoto dev_dbg(component->dev, "%s: enter.\n", __func__);
2573fabe089SMatti J. Aaltonen
258228dd545SMatti J. Aaltonen r = wl1273->core->set_volume(wl1273->core,
2593fabe089SMatti J. Aaltonen ucontrol->value.integer.value[0]);
2603fabe089SMatti J. Aaltonen if (r)
2613fabe089SMatti J. Aaltonen return r;
2623fabe089SMatti J. Aaltonen
2633fabe089SMatti J. Aaltonen return 1;
2643fabe089SMatti J. Aaltonen }
2653fabe089SMatti J. Aaltonen
2663fabe089SMatti J. Aaltonen static const struct snd_kcontrol_new wl1273_controls[] = {
2673fabe089SMatti J. Aaltonen SOC_ENUM_EXT("Codec Mode", wl1273_enum,
2683fabe089SMatti J. Aaltonen snd_wl1273_get_audio_route, snd_wl1273_set_audio_route),
2693fabe089SMatti J. Aaltonen SOC_ENUM_EXT("Audio Switch", wl1273_audio_enum,
2703fabe089SMatti J. Aaltonen snd_wl1273_fm_audio_get, snd_wl1273_fm_audio_put),
2713fabe089SMatti J. Aaltonen SOC_SINGLE_EXT("Volume", 0, 0, WL1273_MAX_VOLUME, 0,
2723fabe089SMatti J. Aaltonen snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
2733fabe089SMatti J. Aaltonen };
2743fabe089SMatti J. Aaltonen
275e29deb48SMark Brown static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
276e29deb48SMark Brown SND_SOC_DAPM_INPUT("RX"),
277e29deb48SMark Brown
278e29deb48SMark Brown SND_SOC_DAPM_OUTPUT("TX"),
279e29deb48SMark Brown };
280e29deb48SMark Brown
281e29deb48SMark Brown static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
282e29deb48SMark Brown { "Capture", NULL, "RX" },
283e29deb48SMark Brown
284e29deb48SMark Brown { "TX", NULL, "Playback" },
285e29deb48SMark Brown };
286e29deb48SMark Brown
wl1273_startup(struct snd_pcm_substream * substream,struct snd_soc_dai * dai)2873fabe089SMatti J. Aaltonen static int wl1273_startup(struct snd_pcm_substream *substream,
2883fabe089SMatti J. Aaltonen struct snd_soc_dai *dai)
2893fabe089SMatti J. Aaltonen {
290559ab397SKuninori Morimoto struct snd_soc_component *component = dai->component;
291559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
2923fabe089SMatti J. Aaltonen
2933fabe089SMatti J. Aaltonen switch (wl1273->mode) {
2943fabe089SMatti J. Aaltonen case WL1273_MODE_BT:
29595c68b86SLars-Peter Clausen snd_pcm_hw_constraint_single(substream->runtime,
29695c68b86SLars-Peter Clausen SNDRV_PCM_HW_PARAM_RATE, 8000);
29795c68b86SLars-Peter Clausen snd_pcm_hw_constraint_single(substream->runtime,
29895c68b86SLars-Peter Clausen SNDRV_PCM_HW_PARAM_CHANNELS, 1);
2993fabe089SMatti J. Aaltonen break;
3003fabe089SMatti J. Aaltonen case WL1273_MODE_FM_RX:
3013fabe089SMatti J. Aaltonen if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3023fabe089SMatti J. Aaltonen pr_err("Cannot play in RX mode.\n");
3033fabe089SMatti J. Aaltonen return -EINVAL;
3043fabe089SMatti J. Aaltonen }
3053fabe089SMatti J. Aaltonen break;
3063fabe089SMatti J. Aaltonen case WL1273_MODE_FM_TX:
3073fabe089SMatti J. Aaltonen if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
3083fabe089SMatti J. Aaltonen pr_err("Cannot capture in TX mode.\n");
3093fabe089SMatti J. Aaltonen return -EINVAL;
3103fabe089SMatti J. Aaltonen }
3113fabe089SMatti J. Aaltonen break;
3123fabe089SMatti J. Aaltonen default:
3133fabe089SMatti J. Aaltonen return -EINVAL;
3143fabe089SMatti J. Aaltonen }
3153fabe089SMatti J. Aaltonen
3163fabe089SMatti J. Aaltonen return 0;
3173fabe089SMatti J. Aaltonen }
3183fabe089SMatti J. Aaltonen
wl1273_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)3193fabe089SMatti J. Aaltonen static int wl1273_hw_params(struct snd_pcm_substream *substream,
3203fabe089SMatti J. Aaltonen struct snd_pcm_hw_params *params,
3213fabe089SMatti J. Aaltonen struct snd_soc_dai *dai)
3223fabe089SMatti J. Aaltonen {
323559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(dai->component);
3243fabe089SMatti J. Aaltonen struct wl1273_core *core = wl1273->core;
3253fabe089SMatti J. Aaltonen unsigned int rate, width, r;
3263fabe089SMatti J. Aaltonen
3279630181aSMark Brown if (params_width(params) != 16) {
3289630181aSMark Brown dev_err(dai->dev, "%d bits/sample not supported\n",
3299630181aSMark Brown params_width(params));
3303fabe089SMatti J. Aaltonen return -EINVAL;
3313fabe089SMatti J. Aaltonen }
3323fabe089SMatti J. Aaltonen
3333fabe089SMatti J. Aaltonen rate = params_rate(params);
3343fabe089SMatti J. Aaltonen width = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
3353fabe089SMatti J. Aaltonen
3363fabe089SMatti J. Aaltonen if (wl1273->mode == WL1273_MODE_BT) {
3373fabe089SMatti J. Aaltonen if (rate != 8000) {
3383fabe089SMatti J. Aaltonen pr_err("Rate %d not supported.\n", params_rate(params));
3393fabe089SMatti J. Aaltonen return -EINVAL;
3403fabe089SMatti J. Aaltonen }
3413fabe089SMatti J. Aaltonen
3423fabe089SMatti J. Aaltonen if (params_channels(params) != 1) {
3433fabe089SMatti J. Aaltonen pr_err("Only mono supported.\n");
3443fabe089SMatti J. Aaltonen return -EINVAL;
3453fabe089SMatti J. Aaltonen }
3463fabe089SMatti J. Aaltonen
3473fabe089SMatti J. Aaltonen return 0;
3483fabe089SMatti J. Aaltonen }
3493fabe089SMatti J. Aaltonen
3503fabe089SMatti J. Aaltonen if (wl1273->mode == WL1273_MODE_FM_TX &&
3513fabe089SMatti J. Aaltonen substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
3523fabe089SMatti J. Aaltonen pr_err("Only playback supported with TX.\n");
3533fabe089SMatti J. Aaltonen return -EINVAL;
3543fabe089SMatti J. Aaltonen }
3553fabe089SMatti J. Aaltonen
3563fabe089SMatti J. Aaltonen if (wl1273->mode == WL1273_MODE_FM_RX &&
3573fabe089SMatti J. Aaltonen substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3583fabe089SMatti J. Aaltonen pr_err("Only capture supported with RX.\n");
3593fabe089SMatti J. Aaltonen return -EINVAL;
3603fabe089SMatti J. Aaltonen }
3613fabe089SMatti J. Aaltonen
3623fabe089SMatti J. Aaltonen if (wl1273->mode != WL1273_MODE_FM_RX &&
3633fabe089SMatti J. Aaltonen wl1273->mode != WL1273_MODE_FM_TX) {
3643fabe089SMatti J. Aaltonen pr_err("Unexpected mode: %d.\n", wl1273->mode);
3653fabe089SMatti J. Aaltonen return -EINVAL;
3663fabe089SMatti J. Aaltonen }
3673fabe089SMatti J. Aaltonen
3683fabe089SMatti J. Aaltonen r = snd_wl1273_fm_set_i2s_mode(core, rate, width);
3693fabe089SMatti J. Aaltonen if (r)
3703fabe089SMatti J. Aaltonen return r;
3713fabe089SMatti J. Aaltonen
3723fabe089SMatti J. Aaltonen wl1273->channels = params_channels(params);
3733fabe089SMatti J. Aaltonen r = snd_wl1273_fm_set_channel_number(core, wl1273->channels);
3743fabe089SMatti J. Aaltonen if (r)
3753fabe089SMatti J. Aaltonen return r;
3763fabe089SMatti J. Aaltonen
3773fabe089SMatti J. Aaltonen return 0;
3783fabe089SMatti J. Aaltonen }
3793fabe089SMatti J. Aaltonen
38085e7652dSLars-Peter Clausen static const struct snd_soc_dai_ops wl1273_dai_ops = {
3813fabe089SMatti J. Aaltonen .startup = wl1273_startup,
3823fabe089SMatti J. Aaltonen .hw_params = wl1273_hw_params,
3833fabe089SMatti J. Aaltonen };
3843fabe089SMatti J. Aaltonen
3853fabe089SMatti J. Aaltonen static struct snd_soc_dai_driver wl1273_dai = {
3863fabe089SMatti J. Aaltonen .name = "wl1273-fm",
3873fabe089SMatti J. Aaltonen .playback = {
3883fabe089SMatti J. Aaltonen .stream_name = "Playback",
3893fabe089SMatti J. Aaltonen .channels_min = 1,
3903fabe089SMatti J. Aaltonen .channels_max = 2,
3913fabe089SMatti J. Aaltonen .rates = SNDRV_PCM_RATE_8000_48000,
3923fabe089SMatti J. Aaltonen .formats = SNDRV_PCM_FMTBIT_S16_LE},
3933fabe089SMatti J. Aaltonen .capture = {
3943fabe089SMatti J. Aaltonen .stream_name = "Capture",
3953fabe089SMatti J. Aaltonen .channels_min = 1,
3963fabe089SMatti J. Aaltonen .channels_max = 2,
3973fabe089SMatti J. Aaltonen .rates = SNDRV_PCM_RATE_8000_48000,
3983fabe089SMatti J. Aaltonen .formats = SNDRV_PCM_FMTBIT_S16_LE},
3993fabe089SMatti J. Aaltonen .ops = &wl1273_dai_ops,
4003fabe089SMatti J. Aaltonen };
4013fabe089SMatti J. Aaltonen
4023fabe089SMatti J. Aaltonen /* Audio interface format for the soc_card driver */
wl1273_get_format(struct snd_soc_component * component,unsigned int * fmt)403559ab397SKuninori Morimoto int wl1273_get_format(struct snd_soc_component *component, unsigned int *fmt)
4043fabe089SMatti J. Aaltonen {
4053fabe089SMatti J. Aaltonen struct wl1273_priv *wl1273;
4063fabe089SMatti J. Aaltonen
407559ab397SKuninori Morimoto if (component == NULL || fmt == NULL)
4083fabe089SMatti J. Aaltonen return -EINVAL;
4093fabe089SMatti J. Aaltonen
410559ab397SKuninori Morimoto wl1273 = snd_soc_component_get_drvdata(component);
4113fabe089SMatti J. Aaltonen
4123fabe089SMatti J. Aaltonen switch (wl1273->mode) {
4133fabe089SMatti J. Aaltonen case WL1273_MODE_FM_RX:
4143fabe089SMatti J. Aaltonen case WL1273_MODE_FM_TX:
4153fabe089SMatti J. Aaltonen *fmt = SND_SOC_DAIFMT_I2S |
4163fabe089SMatti J. Aaltonen SND_SOC_DAIFMT_NB_NF |
417*c9fa2165SMark Brown SND_SOC_DAIFMT_CBP_CFP;
4183fabe089SMatti J. Aaltonen
4193fabe089SMatti J. Aaltonen break;
4203fabe089SMatti J. Aaltonen case WL1273_MODE_BT:
4213fabe089SMatti J. Aaltonen *fmt = SND_SOC_DAIFMT_DSP_A |
4223fabe089SMatti J. Aaltonen SND_SOC_DAIFMT_IB_NF |
423*c9fa2165SMark Brown SND_SOC_DAIFMT_CBP_CFP;
4243fabe089SMatti J. Aaltonen
4253fabe089SMatti J. Aaltonen break;
4263fabe089SMatti J. Aaltonen default:
4273fabe089SMatti J. Aaltonen return -EINVAL;
4283fabe089SMatti J. Aaltonen }
4293fabe089SMatti J. Aaltonen
4303fabe089SMatti J. Aaltonen return 0;
4313fabe089SMatti J. Aaltonen }
4323fabe089SMatti J. Aaltonen EXPORT_SYMBOL_GPL(wl1273_get_format);
4333fabe089SMatti J. Aaltonen
wl1273_probe(struct snd_soc_component * component)434559ab397SKuninori Morimoto static int wl1273_probe(struct snd_soc_component *component)
4353fabe089SMatti J. Aaltonen {
436559ab397SKuninori Morimoto struct wl1273_core **core = component->dev->platform_data;
4373fabe089SMatti J. Aaltonen struct wl1273_priv *wl1273;
4383fabe089SMatti J. Aaltonen
439559ab397SKuninori Morimoto dev_dbg(component->dev, "%s.\n", __func__);
4403fabe089SMatti J. Aaltonen
4413fabe089SMatti J. Aaltonen if (!core) {
442559ab397SKuninori Morimoto dev_err(component->dev, "Platform data is missing.\n");
4433fabe089SMatti J. Aaltonen return -EINVAL;
4443fabe089SMatti J. Aaltonen }
4453fabe089SMatti J. Aaltonen
4463fabe089SMatti J. Aaltonen wl1273 = kzalloc(sizeof(struct wl1273_priv), GFP_KERNEL);
447ac872d3dSSachin Kamat if (!wl1273)
4483fabe089SMatti J. Aaltonen return -ENOMEM;
4493fabe089SMatti J. Aaltonen
4503fabe089SMatti J. Aaltonen wl1273->mode = WL1273_MODE_BT;
4513fabe089SMatti J. Aaltonen wl1273->core = *core;
4523fabe089SMatti J. Aaltonen
453559ab397SKuninori Morimoto snd_soc_component_set_drvdata(component, wl1273);
4543fabe089SMatti J. Aaltonen
455c8b5d089SLars-Peter Clausen return 0;
4563fabe089SMatti J. Aaltonen }
4573fabe089SMatti J. Aaltonen
wl1273_remove(struct snd_soc_component * component)458559ab397SKuninori Morimoto static void wl1273_remove(struct snd_soc_component *component)
4593fabe089SMatti J. Aaltonen {
460559ab397SKuninori Morimoto struct wl1273_priv *wl1273 = snd_soc_component_get_drvdata(component);
4613fabe089SMatti J. Aaltonen
462559ab397SKuninori Morimoto dev_dbg(component->dev, "%s\n", __func__);
4633fabe089SMatti J. Aaltonen kfree(wl1273);
4643fabe089SMatti J. Aaltonen }
4653fabe089SMatti J. Aaltonen
466559ab397SKuninori Morimoto static const struct snd_soc_component_driver soc_component_dev_wl1273 = {
4673fabe089SMatti J. Aaltonen .probe = wl1273_probe,
4683fabe089SMatti J. Aaltonen .remove = wl1273_remove,
469c8b5d089SLars-Peter Clausen .controls = wl1273_controls,
470c8b5d089SLars-Peter Clausen .num_controls = ARRAY_SIZE(wl1273_controls),
471e29deb48SMark Brown .dapm_widgets = wl1273_dapm_widgets,
472e29deb48SMark Brown .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
473e29deb48SMark Brown .dapm_routes = wl1273_dapm_routes,
474e29deb48SMark Brown .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes),
475559ab397SKuninori Morimoto .idle_bias_on = 1,
476559ab397SKuninori Morimoto .use_pmdown_time = 1,
477559ab397SKuninori Morimoto .endianness = 1,
4783fabe089SMatti J. Aaltonen };
4793fabe089SMatti J. Aaltonen
wl1273_platform_probe(struct platform_device * pdev)4807a79e94eSBill Pemberton static int wl1273_platform_probe(struct platform_device *pdev)
4813fabe089SMatti J. Aaltonen {
482559ab397SKuninori Morimoto return devm_snd_soc_register_component(&pdev->dev,
483559ab397SKuninori Morimoto &soc_component_dev_wl1273,
4843fabe089SMatti J. Aaltonen &wl1273_dai, 1);
4853fabe089SMatti J. Aaltonen }
4863fabe089SMatti J. Aaltonen
4873fabe089SMatti J. Aaltonen MODULE_ALIAS("platform:wl1273-codec");
4883fabe089SMatti J. Aaltonen
4893fabe089SMatti J. Aaltonen static struct platform_driver wl1273_platform_driver = {
4903fabe089SMatti J. Aaltonen .driver = {
4913fabe089SMatti J. Aaltonen .name = "wl1273-codec",
4923fabe089SMatti J. Aaltonen },
4933fabe089SMatti J. Aaltonen .probe = wl1273_platform_probe,
4943fabe089SMatti J. Aaltonen };
4953fabe089SMatti J. Aaltonen
4965bbcc3c0SMark Brown module_platform_driver(wl1273_platform_driver);
4973fabe089SMatti J. Aaltonen
4983fabe089SMatti J. Aaltonen MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>");
4993fabe089SMatti J. Aaltonen MODULE_DESCRIPTION("ASoC WL1273 codec driver");
5003fabe089SMatti J. Aaltonen MODULE_LICENSE("GPL");
501