brintos

brintos / linux-shallow public Read only

0
0
Text · 11.0 KiB · 710b674 Raw
440 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) ST-Ericsson SA 20124 *5 * Author: Ola Lilja <ola.o.lilja@stericsson.com>,6 *         Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>7 *         for ST-Ericsson.8 */9 10#include <linux/module.h>11#include <linux/device.h>12#include <linux/io.h>13#include <linux/clk.h>14#include <linux/mutex.h>15 16#include <sound/soc.h>17#include <sound/soc-dapm.h>18#include <sound/pcm.h>19#include <sound/pcm_params.h>20 21#include "ux500_pcm.h"22#include "ux500_msp_dai.h"23#include "mop500_ab8500.h"24#include "../codecs/ab8500-codec.h"25 26#define TX_SLOT_MONO	0x000827#define TX_SLOT_STEREO	0x000a28#define RX_SLOT_MONO	0x000129#define RX_SLOT_STEREO	0x000330#define TX_SLOT_8CH	0x00FF31#define RX_SLOT_8CH	0x00FF32 33#define DEF_TX_SLOTS	TX_SLOT_STEREO34#define DEF_RX_SLOTS	RX_SLOT_MONO35 36#define DRIVERMODE_NORMAL	037#define DRIVERMODE_CODEC_ONLY	138 39/* Slot configuration */40static unsigned int tx_slots = DEF_TX_SLOTS;41static unsigned int rx_slots = DEF_RX_SLOTS;42 43/* Configuration consistency parameters */44static DEFINE_MUTEX(mop500_ab8500_params_lock);45static unsigned long mop500_ab8500_usage;46static int mop500_ab8500_rate;47static int mop500_ab8500_channels;48 49/* Clocks */50static const char * const enum_mclk[] = {51	"SYSCLK",52	"ULPCLK"53};54enum mclk {55	MCLK_SYSCLK,56	MCLK_ULPCLK,57};58 59static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_mclk, enum_mclk);60 61/* Private data for machine-part MOP500<->AB8500 */62struct mop500_ab8500_drvdata {63	/* Clocks */64	enum mclk mclk_sel;65	struct clk *clk_ptr_intclk;66	struct clk *clk_ptr_sysclk;67	struct clk *clk_ptr_ulpclk;68};69 70static inline const char *get_mclk_str(enum mclk mclk_sel)71{72	switch (mclk_sel) {73	case MCLK_SYSCLK:74		return "SYSCLK";75	case MCLK_ULPCLK:76		return "ULPCLK";77	default:78		return "Unknown";79	}80}81 82static int mop500_ab8500_set_mclk(struct device *dev,83				struct mop500_ab8500_drvdata *drvdata)84{85	int status;86	struct clk *clk_ptr;87 88	if (IS_ERR(drvdata->clk_ptr_intclk)) {89		dev_err(dev,90			"%s: ERROR: intclk not initialized!\n", __func__);91		return -EIO;92	}93 94	switch (drvdata->mclk_sel) {95	case MCLK_SYSCLK:96		clk_ptr = drvdata->clk_ptr_sysclk;97		break;98	case MCLK_ULPCLK:99		clk_ptr = drvdata->clk_ptr_ulpclk;100		break;101	default:102		return -EINVAL;103	}104 105	if (IS_ERR(clk_ptr)) {106		dev_err(dev, "%s: ERROR: %s not initialized!\n", __func__,107			get_mclk_str(drvdata->mclk_sel));108		return -EIO;109	}110 111	status = clk_set_parent(drvdata->clk_ptr_intclk, clk_ptr);112	if (status)113		dev_err(dev,114			"%s: ERROR: Setting intclk parent to %s failed (ret = %d)!",115			__func__, get_mclk_str(drvdata->mclk_sel), status);116	else117		dev_dbg(dev,118			"%s: intclk parent changed to %s.\n",119			__func__, get_mclk_str(drvdata->mclk_sel));120 121	return status;122}123 124/*125 * Control-events126 */127 128static int mclk_input_control_get(struct snd_kcontrol *kcontrol,129				struct snd_ctl_elem_value *ucontrol)130{131	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);132	struct mop500_ab8500_drvdata *drvdata =133				snd_soc_card_get_drvdata(card);134 135	ucontrol->value.enumerated.item[0] = drvdata->mclk_sel;136 137	return 0;138}139 140static int mclk_input_control_put(struct snd_kcontrol *kcontrol,141				struct snd_ctl_elem_value *ucontrol)142{143	struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);144	struct mop500_ab8500_drvdata *drvdata =145				snd_soc_card_get_drvdata(card);146	unsigned int val = ucontrol->value.enumerated.item[0];147 148	if (val > (unsigned int)MCLK_ULPCLK)149		return -EINVAL;150	if (drvdata->mclk_sel == val)151		return 0;152 153	drvdata->mclk_sel = val;154 155	return 1;156}157 158/*159 * Controls160 */161 162static struct snd_kcontrol_new mop500_ab8500_ctrls[] = {163	SOC_ENUM_EXT("Master Clock Select",164		soc_enum_mclk,165		mclk_input_control_get, mclk_input_control_put),166	SOC_DAPM_PIN_SWITCH("Headset Left"),167	SOC_DAPM_PIN_SWITCH("Headset Right"),168	SOC_DAPM_PIN_SWITCH("Earpiece"),169	SOC_DAPM_PIN_SWITCH("Speaker Left"),170	SOC_DAPM_PIN_SWITCH("Speaker Right"),171	SOC_DAPM_PIN_SWITCH("LineOut Left"),172	SOC_DAPM_PIN_SWITCH("LineOut Right"),173	SOC_DAPM_PIN_SWITCH("Vibra 1"),174	SOC_DAPM_PIN_SWITCH("Vibra 2"),175	SOC_DAPM_PIN_SWITCH("Mic 1"),176	SOC_DAPM_PIN_SWITCH("Mic 2"),177	SOC_DAPM_PIN_SWITCH("LineIn Left"),178	SOC_DAPM_PIN_SWITCH("LineIn Right"),179	SOC_DAPM_PIN_SWITCH("DMic 1"),180	SOC_DAPM_PIN_SWITCH("DMic 2"),181	SOC_DAPM_PIN_SWITCH("DMic 3"),182	SOC_DAPM_PIN_SWITCH("DMic 4"),183	SOC_DAPM_PIN_SWITCH("DMic 5"),184	SOC_DAPM_PIN_SWITCH("DMic 6"),185};186 187/* ASoC */188 189static int mop500_ab8500_startup(struct snd_pcm_substream *substream)190{191	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);192 193	/* Set audio-clock source */194	return mop500_ab8500_set_mclk(rtd->card->dev,195				snd_soc_card_get_drvdata(rtd->card));196}197 198static void mop500_ab8500_shutdown(struct snd_pcm_substream *substream)199{200	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);201	struct device *dev = rtd->card->dev;202 203	dev_dbg(dev, "%s: Enter\n", __func__);204 205	/* Reset slots configuration to default(s) */206	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)207		tx_slots = DEF_TX_SLOTS;208	else209		rx_slots = DEF_RX_SLOTS;210}211 212static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream,213			struct snd_pcm_hw_params *params)214{215	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);216	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);217	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);218	struct device *dev = rtd->card->dev;219	unsigned int fmt;220	int channels, ret = 0, driver_mode, slots;221	unsigned int sw_codec, sw_cpu;222	bool is_playback;223 224	dev_dbg(dev, "%s: Enter\n", __func__);225 226	dev_dbg(dev, "%s: substream->pcm->name = %s\n"227		"substream->pcm->id = %s.\n"228		"substream->name = %s.\n"229		"substream->number = %d.\n",230		__func__,231		substream->pcm->name,232		substream->pcm->id,233		substream->name,234		substream->number);235 236	/* Ensure configuration consistency between DAIs */237	mutex_lock(&mop500_ab8500_params_lock);238	if (mop500_ab8500_usage) {239		if (mop500_ab8500_rate != params_rate(params) ||240		    mop500_ab8500_channels != params_channels(params)) {241			mutex_unlock(&mop500_ab8500_params_lock);242			return -EBUSY;243		}244	} else {245		mop500_ab8500_rate = params_rate(params);246		mop500_ab8500_channels = params_channels(params);247	}248	__set_bit(cpu_dai->id, &mop500_ab8500_usage);249	mutex_unlock(&mop500_ab8500_params_lock);250 251	channels = params_channels(params);252 253	switch (params_format(params)) {254	case SNDRV_PCM_FORMAT_S32_LE:255		sw_cpu = 32;256		break;257 258	case SNDRV_PCM_FORMAT_S16_LE:259		sw_cpu = 16;260		break;261 262	default:263		return -EINVAL;264	}265 266	/* Setup codec depending on driver-mode */267	if (channels == 8)268		driver_mode = DRIVERMODE_CODEC_ONLY;269	else270		driver_mode = DRIVERMODE_NORMAL;271	dev_dbg(dev, "%s: Driver-mode: %s.\n", __func__,272		(driver_mode == DRIVERMODE_NORMAL) ? "NORMAL" : "CODEC_ONLY");273 274	/* Setup format */275 276	if (driver_mode == DRIVERMODE_NORMAL) {277		fmt = SND_SOC_DAIFMT_DSP_A |278			SND_SOC_DAIFMT_CBM_CFM |279			SND_SOC_DAIFMT_NB_NF |280			SND_SOC_DAIFMT_CONT;281	} else {282		fmt = SND_SOC_DAIFMT_DSP_A |283			SND_SOC_DAIFMT_CBM_CFM |284			SND_SOC_DAIFMT_NB_NF |285			SND_SOC_DAIFMT_GATED;286	}287 288	ret = snd_soc_runtime_set_dai_fmt(rtd, fmt);289	if (ret)290		return ret;291 292	/* Setup TDM-slots */293 294	is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);295	switch (channels) {296	case 1:297		slots = 16;298		tx_slots = (is_playback) ? TX_SLOT_MONO : 0;299		rx_slots = (is_playback) ? 0 : RX_SLOT_MONO;300		break;301	case 2:302		slots = 16;303		tx_slots = (is_playback) ? TX_SLOT_STEREO : 0;304		rx_slots = (is_playback) ? 0 : RX_SLOT_STEREO;305		break;306	case 8:307		slots = 16;308		tx_slots = (is_playback) ? TX_SLOT_8CH : 0;309		rx_slots = (is_playback) ? 0 : RX_SLOT_8CH;310		break;311	default:312		return -EINVAL;313	}314 315	if (driver_mode == DRIVERMODE_NORMAL)316		sw_codec = sw_cpu;317	else318		sw_codec = 20;319 320	dev_dbg(dev, "%s: CPU-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__,321		tx_slots, rx_slots);322	ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_slots, rx_slots, slots,323				sw_cpu);324	if (ret)325		return ret;326 327	dev_dbg(dev, "%s: CODEC-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__,328		tx_slots, rx_slots);329	ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_slots, rx_slots, slots,330				sw_codec);331	if (ret)332		return ret;333 334	return 0;335}336 337static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream)338{339	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);340	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);341 342	mutex_lock(&mop500_ab8500_params_lock);343	__clear_bit(cpu_dai->id, &mop500_ab8500_usage);344	mutex_unlock(&mop500_ab8500_params_lock);345 346	return 0;347}348 349const struct snd_soc_ops mop500_ab8500_ops[] = {350	{351		.hw_params = mop500_ab8500_hw_params,352		.hw_free = mop500_ab8500_hw_free,353		.startup = mop500_ab8500_startup,354		.shutdown = mop500_ab8500_shutdown,355	}356};357 358int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd)359{360	struct snd_soc_dapm_context *dapm = &rtd->card->dapm;361	struct device *dev = rtd->card->dev;362	struct mop500_ab8500_drvdata *drvdata;363	int ret;364 365	dev_dbg(dev, "%s Enter.\n", __func__);366 367	/* Create driver private-data struct */368	drvdata = devm_kzalloc(dev, sizeof(struct mop500_ab8500_drvdata),369			GFP_KERNEL);370 371	if (!drvdata)372		return -ENOMEM;373 374	snd_soc_card_set_drvdata(rtd->card, drvdata);375 376	/* Setup clocks */377 378	drvdata->clk_ptr_sysclk = clk_get(dev, "sysclk");379	if (IS_ERR(drvdata->clk_ptr_sysclk))380		dev_warn(dev, "%s: WARNING: clk_get failed for 'sysclk'!\n",381			__func__);382	drvdata->clk_ptr_ulpclk = clk_get(dev, "ulpclk");383	if (IS_ERR(drvdata->clk_ptr_ulpclk))384		dev_warn(dev, "%s: WARNING: clk_get failed for 'ulpclk'!\n",385			__func__);386	drvdata->clk_ptr_intclk = clk_get(dev, "intclk");387	if (IS_ERR(drvdata->clk_ptr_intclk))388		dev_warn(dev, "%s: WARNING: clk_get failed for 'intclk'!\n",389			__func__);390 391	/* Set intclk default parent to ulpclk */392	drvdata->mclk_sel = MCLK_ULPCLK;393	ret = mop500_ab8500_set_mclk(dev, drvdata);394	if (ret < 0)395		dev_warn(dev, "%s: WARNING: mop500_ab8500_set_mclk!\n",396			__func__);397 398	drvdata->mclk_sel = MCLK_ULPCLK;399 400	/* Add controls */401	ret = snd_soc_add_card_controls(rtd->card, mop500_ab8500_ctrls,402			ARRAY_SIZE(mop500_ab8500_ctrls));403	if (ret < 0) {404		pr_err("%s: Failed to add machine-controls (%d)!\n",405				__func__, ret);406		return ret;407	}408 409	ret = snd_soc_dapm_disable_pin(dapm, "Earpiece");410	ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Left");411	ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Right");412	ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Left");413	ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Right");414	ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 1");415	ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 2");416	ret |= snd_soc_dapm_disable_pin(dapm, "Mic 1");417	ret |= snd_soc_dapm_disable_pin(dapm, "Mic 2");418	ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Left");419	ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Right");420	ret |= snd_soc_dapm_disable_pin(dapm, "DMic 1");421	ret |= snd_soc_dapm_disable_pin(dapm, "DMic 2");422	ret |= snd_soc_dapm_disable_pin(dapm, "DMic 3");423	ret |= snd_soc_dapm_disable_pin(dapm, "DMic 4");424	ret |= snd_soc_dapm_disable_pin(dapm, "DMic 5");425	ret |= snd_soc_dapm_disable_pin(dapm, "DMic 6");426 427	return ret;428}429 430void mop500_ab8500_remove(struct snd_soc_card *card)431{432	struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card);433 434	clk_put(drvdata->clk_ptr_sysclk);435	clk_put(drvdata->clk_ptr_ulpclk);436	clk_put(drvdata->clk_ptr_intclk);437 438	snd_soc_card_set_drvdata(card, drvdata);439}440