18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2015 Atmel
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author: Songjun Wu <songjun.wu@atmel.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/clk.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <sound/core.h>
158c2ecf20Sopenharmony_ci#include <sound/dmaengine_pcm.h>
168c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
178c2ecf20Sopenharmony_ci#include <sound/tlv.h>
188c2ecf20Sopenharmony_ci#include "atmel-classd.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct atmel_classd_pdata {
218c2ecf20Sopenharmony_ci	bool non_overlap_enable;
228c2ecf20Sopenharmony_ci	int non_overlap_time;
238c2ecf20Sopenharmony_ci	int pwm_type;
248c2ecf20Sopenharmony_ci	const char *card_name;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistruct atmel_classd {
288c2ecf20Sopenharmony_ci	dma_addr_t phy_base;
298c2ecf20Sopenharmony_ci	struct regmap *regmap;
308c2ecf20Sopenharmony_ci	struct clk *pclk;
318c2ecf20Sopenharmony_ci	struct clk *gclk;
328c2ecf20Sopenharmony_ci	struct device *dev;
338c2ecf20Sopenharmony_ci	int irq;
348c2ecf20Sopenharmony_ci	const struct atmel_classd_pdata *pdata;
358c2ecf20Sopenharmony_ci};
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#ifdef CONFIG_OF
388c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_classd_of_match[] = {
398c2ecf20Sopenharmony_ci	{
408c2ecf20Sopenharmony_ci		.compatible = "atmel,sama5d2-classd",
418c2ecf20Sopenharmony_ci	}, {
428c2ecf20Sopenharmony_ci		/* sentinel */
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, atmel_classd_of_match);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
508c2ecf20Sopenharmony_ci	struct atmel_classd_pdata *pdata;
518c2ecf20Sopenharmony_ci	const char *pwm_type;
528c2ecf20Sopenharmony_ci	int ret;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!np) {
558c2ecf20Sopenharmony_ci		dev_err(dev, "device node not found\n");
568c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
608c2ecf20Sopenharmony_ci	if (!pdata)
618c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
648c2ecf20Sopenharmony_ci	if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
658c2ecf20Sopenharmony_ci		pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
668c2ecf20Sopenharmony_ci	else
678c2ecf20Sopenharmony_ci		pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	ret = of_property_read_u32(np,
708c2ecf20Sopenharmony_ci			"atmel,non-overlap-time", &pdata->non_overlap_time);
718c2ecf20Sopenharmony_ci	if (ret)
728c2ecf20Sopenharmony_ci		pdata->non_overlap_enable = false;
738c2ecf20Sopenharmony_ci	else
748c2ecf20Sopenharmony_ci		pdata->non_overlap_enable = true;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
778c2ecf20Sopenharmony_ci	if (ret)
788c2ecf20Sopenharmony_ci		pdata->card_name = "CLASSD";
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	return pdata;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci#else
838c2ecf20Sopenharmony_cistatic inline struct atmel_classd_pdata *
848c2ecf20Sopenharmony_ciatmel_classd_dt_init(struct device *dev)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	return ERR_PTR(-EINVAL);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
918c2ecf20Sopenharmony_ci			| SNDRV_PCM_RATE_16000	| SNDRV_PCM_RATE_22050 \
928c2ecf20Sopenharmony_ci			| SNDRV_PCM_RATE_32000	| SNDRV_PCM_RATE_44100 \
938c2ecf20Sopenharmony_ci			| SNDRV_PCM_RATE_48000	| SNDRV_PCM_RATE_88200 \
948c2ecf20Sopenharmony_ci			| SNDRV_PCM_RATE_96000)
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware atmel_classd_hw = {
978c2ecf20Sopenharmony_ci	.info			= SNDRV_PCM_INFO_MMAP
988c2ecf20Sopenharmony_ci				| SNDRV_PCM_INFO_MMAP_VALID
998c2ecf20Sopenharmony_ci				| SNDRV_PCM_INFO_INTERLEAVED
1008c2ecf20Sopenharmony_ci				| SNDRV_PCM_INFO_RESUME
1018c2ecf20Sopenharmony_ci				| SNDRV_PCM_INFO_PAUSE,
1028c2ecf20Sopenharmony_ci	.formats		= (SNDRV_PCM_FMTBIT_S16_LE),
1038c2ecf20Sopenharmony_ci	.rates			= ATMEL_CLASSD_RATES,
1048c2ecf20Sopenharmony_ci	.rate_min		= 8000,
1058c2ecf20Sopenharmony_ci	.rate_max		= 96000,
1068c2ecf20Sopenharmony_ci	.channels_min		= 1,
1078c2ecf20Sopenharmony_ci	.channels_max		= 2,
1088c2ecf20Sopenharmony_ci	.buffer_bytes_max	= 64 * 1024,
1098c2ecf20Sopenharmony_ci	.period_bytes_min	= 256,
1108c2ecf20Sopenharmony_ci	.period_bytes_max	= 32 * 1024,
1118c2ecf20Sopenharmony_ci	.periods_min		= 2,
1128c2ecf20Sopenharmony_ci	.periods_max		= 256,
1138c2ecf20Sopenharmony_ci};
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#define ATMEL_CLASSD_PREALLOC_BUF_SIZE  (64 * 1024)
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* cpu dai component */
1188c2ecf20Sopenharmony_cistatic int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
1198c2ecf20Sopenharmony_ci					struct snd_soc_dai *cpu_dai)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1228c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
1238c2ecf20Sopenharmony_ci	int err;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	regmap_write(dd->regmap, CLASSD_THR, 0x0);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dd->pclk);
1288c2ecf20Sopenharmony_ci	if (err)
1298c2ecf20Sopenharmony_ci		return err;
1308c2ecf20Sopenharmony_ci	err = clk_prepare_enable(dd->gclk);
1318c2ecf20Sopenharmony_ci	if (err) {
1328c2ecf20Sopenharmony_ci		clk_disable_unprepare(dd->pclk);
1338c2ecf20Sopenharmony_ci		return err;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* platform */
1398c2ecf20Sopenharmony_cistatic int
1408c2ecf20Sopenharmony_ciatmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
1418c2ecf20Sopenharmony_ci	struct snd_pcm_hw_params *params,
1428c2ecf20Sopenharmony_ci	struct dma_slave_config *slave_config)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1458c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (params_physical_width(params) != 16) {
1488c2ecf20Sopenharmony_ci		dev_err(dd->dev,
1498c2ecf20Sopenharmony_ci			"only supports 16-bit audio data\n");
1508c2ecf20Sopenharmony_ci		return -EINVAL;
1518c2ecf20Sopenharmony_ci	}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (params_channels(params) == 1)
1548c2ecf20Sopenharmony_ci		slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
1558c2ecf20Sopenharmony_ci	else
1568c2ecf20Sopenharmony_ci		slave_config->dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	slave_config->direction		= DMA_MEM_TO_DEV;
1598c2ecf20Sopenharmony_ci	slave_config->dst_addr		= dd->phy_base + CLASSD_THR;
1608c2ecf20Sopenharmony_ci	slave_config->dst_maxburst	= 1;
1618c2ecf20Sopenharmony_ci	slave_config->src_maxburst	= 1;
1628c2ecf20Sopenharmony_ci	slave_config->device_fc		= false;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic const struct snd_dmaengine_pcm_config
1688c2ecf20Sopenharmony_ciatmel_classd_dmaengine_pcm_config = {
1698c2ecf20Sopenharmony_ci	.prepare_slave_config	= atmel_classd_platform_configure_dma,
1708c2ecf20Sopenharmony_ci	.pcm_hardware		= &atmel_classd_hw,
1718c2ecf20Sopenharmony_ci	.prealloc_buffer_size	= ATMEL_CLASSD_PREALLOC_BUF_SIZE,
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci/* codec */
1758c2ecf20Sopenharmony_cistatic const char * const mono_mode_text[] = {
1768c2ecf20Sopenharmony_ci	"mix", "sat", "left", "right"
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
1808c2ecf20Sopenharmony_ci			CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
1818c2ecf20Sopenharmony_ci			mono_mode_text);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const char * const eqcfg_text[] = {
1848c2ecf20Sopenharmony_ci	"Treble-12dB", "Treble-6dB",
1858c2ecf20Sopenharmony_ci	"Medium-8dB", "Medium-3dB",
1868c2ecf20Sopenharmony_ci	"Bass-12dB", "Bass-6dB",
1878c2ecf20Sopenharmony_ci	"0 dB",
1888c2ecf20Sopenharmony_ci	"Bass+6dB", "Bass+12dB",
1898c2ecf20Sopenharmony_ci	"Medium+3dB", "Medium+8dB",
1908c2ecf20Sopenharmony_ci	"Treble+6dB", "Treble+12dB",
1918c2ecf20Sopenharmony_ci};
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic const unsigned int eqcfg_value[] = {
1948c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
1958c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
1968c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
1978c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_FLAT,
1988c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
1998c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
2008c2ecf20Sopenharmony_ci	CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
2018c2ecf20Sopenharmony_ci};
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
2048c2ecf20Sopenharmony_ci		CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
2058c2ecf20Sopenharmony_ci		eqcfg_text, eqcfg_value);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
2108c2ecf20Sopenharmony_ciSOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
2118c2ecf20Sopenharmony_ci		CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
2128c2ecf20Sopenharmony_ci		78, 1, classd_digital_tlv),
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ciSOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
2158c2ecf20Sopenharmony_ci		CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciSOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ciSOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ciSOC_ENUM("Mono Mode", classd_mono_mode_enum),
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciSOC_ENUM("EQ", classd_eqcfg_enum),
2248c2ecf20Sopenharmony_ci};
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic const char * const pwm_type[] = {
2278c2ecf20Sopenharmony_ci	"Single ended", "Differential"
2288c2ecf20Sopenharmony_ci};
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int atmel_classd_component_probe(struct snd_soc_component *component)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
2338c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
2348c2ecf20Sopenharmony_ci	const struct atmel_classd_pdata *pdata = dd->pdata;
2358c2ecf20Sopenharmony_ci	u32 mask, val;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mask = CLASSD_MR_PWMTYP_MASK;
2388c2ecf20Sopenharmony_ci	val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	mask |= CLASSD_MR_NON_OVERLAP_MASK;
2418c2ecf20Sopenharmony_ci	if (pdata->non_overlap_enable) {
2428c2ecf20Sopenharmony_ci		val |= (CLASSD_MR_NON_OVERLAP_EN
2438c2ecf20Sopenharmony_ci			<< CLASSD_MR_NON_OVERLAP_SHIFT);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		mask |= CLASSD_MR_NOVR_VAL_MASK;
2468c2ecf20Sopenharmony_ci		switch (pdata->non_overlap_time) {
2478c2ecf20Sopenharmony_ci		case 5:
2488c2ecf20Sopenharmony_ci			val |= (CLASSD_MR_NOVR_VAL_5NS
2498c2ecf20Sopenharmony_ci				<< CLASSD_MR_NOVR_VAL_SHIFT);
2508c2ecf20Sopenharmony_ci			break;
2518c2ecf20Sopenharmony_ci		case 10:
2528c2ecf20Sopenharmony_ci			val |= (CLASSD_MR_NOVR_VAL_10NS
2538c2ecf20Sopenharmony_ci				<< CLASSD_MR_NOVR_VAL_SHIFT);
2548c2ecf20Sopenharmony_ci			break;
2558c2ecf20Sopenharmony_ci		case 15:
2568c2ecf20Sopenharmony_ci			val |= (CLASSD_MR_NOVR_VAL_15NS
2578c2ecf20Sopenharmony_ci				<< CLASSD_MR_NOVR_VAL_SHIFT);
2588c2ecf20Sopenharmony_ci			break;
2598c2ecf20Sopenharmony_ci		case 20:
2608c2ecf20Sopenharmony_ci			val |= (CLASSD_MR_NOVR_VAL_20NS
2618c2ecf20Sopenharmony_ci				<< CLASSD_MR_NOVR_VAL_SHIFT);
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci		default:
2648c2ecf20Sopenharmony_ci			val |= (CLASSD_MR_NOVR_VAL_10NS
2658c2ecf20Sopenharmony_ci				<< CLASSD_MR_NOVR_VAL_SHIFT);
2668c2ecf20Sopenharmony_ci			dev_warn(component->dev,
2678c2ecf20Sopenharmony_ci				"non-overlapping value %d is invalid, the default value 10 is specified\n",
2688c2ecf20Sopenharmony_ci				pdata->non_overlap_time);
2698c2ecf20Sopenharmony_ci			break;
2708c2ecf20Sopenharmony_ci		}
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	dev_info(component->dev,
2768c2ecf20Sopenharmony_ci		"PWM modulation type is %s, non-overlapping is %s\n",
2778c2ecf20Sopenharmony_ci		pwm_type[pdata->pwm_type],
2788c2ecf20Sopenharmony_ci		pdata->non_overlap_enable?"enabled":"disabled");
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return 0;
2818c2ecf20Sopenharmony_ci}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int atmel_classd_component_resume(struct snd_soc_component *component)
2848c2ecf20Sopenharmony_ci{
2858c2ecf20Sopenharmony_ci	struct snd_soc_card *card = snd_soc_component_get_drvdata(component);
2868c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	return regcache_sync(dd->regmap);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int atmel_classd_cpu_dai_mute_stream(struct snd_soc_dai *cpu_dai,
2928c2ecf20Sopenharmony_ci					    int mute, int direction)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	struct snd_soc_component *component = cpu_dai->component;
2958c2ecf20Sopenharmony_ci	u32 mask, val;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (mute)
3008c2ecf20Sopenharmony_ci		val = mask;
3018c2ecf20Sopenharmony_ci	else
3028c2ecf20Sopenharmony_ci		val = 0;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	return 0;
3078c2ecf20Sopenharmony_ci}
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
3108c2ecf20Sopenharmony_ci#define CLASSD_GCLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic struct {
3138c2ecf20Sopenharmony_ci	int rate;
3148c2ecf20Sopenharmony_ci	int sample_rate;
3158c2ecf20Sopenharmony_ci	int dsp_clk;
3168c2ecf20Sopenharmony_ci	unsigned long gclk_rate;
3178c2ecf20Sopenharmony_ci} const sample_rates[] = {
3188c2ecf20Sopenharmony_ci	{ 8000,  CLASSD_INTPMR_FRAME_8K,
3198c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
3208c2ecf20Sopenharmony_ci	{ 16000, CLASSD_INTPMR_FRAME_16K,
3218c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
3228c2ecf20Sopenharmony_ci	{ 32000, CLASSD_INTPMR_FRAME_32K,
3238c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
3248c2ecf20Sopenharmony_ci	{ 48000, CLASSD_INTPMR_FRAME_48K,
3258c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
3268c2ecf20Sopenharmony_ci	{ 96000, CLASSD_INTPMR_FRAME_96K,
3278c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
3288c2ecf20Sopenharmony_ci	{ 22050, CLASSD_INTPMR_FRAME_22K,
3298c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
3308c2ecf20Sopenharmony_ci	{ 44100, CLASSD_INTPMR_FRAME_44K,
3318c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
3328c2ecf20Sopenharmony_ci	{ 88200, CLASSD_INTPMR_FRAME_88K,
3338c2ecf20Sopenharmony_ci	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
3348c2ecf20Sopenharmony_ci};
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int
3378c2ecf20Sopenharmony_ciatmel_classd_cpu_dai_hw_params(struct snd_pcm_substream *substream,
3388c2ecf20Sopenharmony_ci			       struct snd_pcm_hw_params *params,
3398c2ecf20Sopenharmony_ci			       struct snd_soc_dai *cpu_dai)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3428c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
3438c2ecf20Sopenharmony_ci	struct snd_soc_component *component = cpu_dai->component;
3448c2ecf20Sopenharmony_ci	int fs;
3458c2ecf20Sopenharmony_ci	int i, best, best_val, cur_val, ret;
3468c2ecf20Sopenharmony_ci	u32 mask, val;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	fs = params_rate(params);
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	best = 0;
3518c2ecf20Sopenharmony_ci	best_val = abs(fs - sample_rates[0].rate);
3528c2ecf20Sopenharmony_ci	for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
3538c2ecf20Sopenharmony_ci		/* Closest match */
3548c2ecf20Sopenharmony_ci		cur_val = abs(fs - sample_rates[i].rate);
3558c2ecf20Sopenharmony_ci		if (cur_val < best_val) {
3568c2ecf20Sopenharmony_ci			best = i;
3578c2ecf20Sopenharmony_ci			best_val = cur_val;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci	}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	dev_dbg(component->dev,
3628c2ecf20Sopenharmony_ci		"Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
3638c2ecf20Sopenharmony_ci		sample_rates[best].rate, sample_rates[best].gclk_rate);
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	clk_disable_unprepare(dd->gclk);
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
3688c2ecf20Sopenharmony_ci	if (ret)
3698c2ecf20Sopenharmony_ci		return ret;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
3728c2ecf20Sopenharmony_ci	val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
3738c2ecf20Sopenharmony_ci	| (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, CLASSD_INTPMR, mask, val);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	return clk_prepare_enable(dd->gclk);
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_cistatic void
3818c2ecf20Sopenharmony_ciatmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
3828c2ecf20Sopenharmony_ci			      struct snd_soc_dai *cpu_dai)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
3858c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	clk_disable_unprepare(dd->gclk);
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic int atmel_classd_cpu_dai_prepare(struct snd_pcm_substream *substream,
3918c2ecf20Sopenharmony_ci					struct snd_soc_dai *cpu_dai)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	struct snd_soc_component *component = cpu_dai->component;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, CLASSD_MR,
3968c2ecf20Sopenharmony_ci				CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
3978c2ecf20Sopenharmony_ci				(CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
3988c2ecf20Sopenharmony_ci				|(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic int atmel_classd_cpu_dai_trigger(struct snd_pcm_substream *substream,
4048c2ecf20Sopenharmony_ci					int cmd, struct snd_soc_dai *cpu_dai)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct snd_soc_component *component = cpu_dai->component;
4078c2ecf20Sopenharmony_ci	u32 mask, val;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	switch (cmd) {
4128c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
4138c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
4148c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
4158c2ecf20Sopenharmony_ci		val = mask;
4168c2ecf20Sopenharmony_ci		break;
4178c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
4188c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
4198c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
4208c2ecf20Sopenharmony_ci		val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
4218c2ecf20Sopenharmony_ci			| (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
4228c2ecf20Sopenharmony_ci		break;
4238c2ecf20Sopenharmony_ci	default:
4248c2ecf20Sopenharmony_ci		return -EINVAL;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	snd_soc_component_update_bits(component, CLASSD_MR, mask, val);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	return 0;
4308c2ecf20Sopenharmony_ci}
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
4338c2ecf20Sopenharmony_ci	.startup        = atmel_classd_cpu_dai_startup,
4348c2ecf20Sopenharmony_ci	.shutdown       = atmel_classd_cpu_dai_shutdown,
4358c2ecf20Sopenharmony_ci	.mute_stream	= atmel_classd_cpu_dai_mute_stream,
4368c2ecf20Sopenharmony_ci	.hw_params	= atmel_classd_cpu_dai_hw_params,
4378c2ecf20Sopenharmony_ci	.prepare	= atmel_classd_cpu_dai_prepare,
4388c2ecf20Sopenharmony_ci	.trigger	= atmel_classd_cpu_dai_trigger,
4398c2ecf20Sopenharmony_ci	.no_capture_mute = 1,
4408c2ecf20Sopenharmony_ci};
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver atmel_classd_cpu_dai = {
4438c2ecf20Sopenharmony_ci	.playback = {
4448c2ecf20Sopenharmony_ci		.stream_name	= "Playback",
4458c2ecf20Sopenharmony_ci		.channels_min	= 1,
4468c2ecf20Sopenharmony_ci		.channels_max	= 2,
4478c2ecf20Sopenharmony_ci		.rates		= ATMEL_CLASSD_RATES,
4488c2ecf20Sopenharmony_ci		.formats	= SNDRV_PCM_FMTBIT_S16_LE,
4498c2ecf20Sopenharmony_ci	},
4508c2ecf20Sopenharmony_ci	.ops = &atmel_classd_cpu_dai_ops,
4518c2ecf20Sopenharmony_ci};
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
4548c2ecf20Sopenharmony_ci	.name			= "atmel-classd",
4558c2ecf20Sopenharmony_ci	.probe			= atmel_classd_component_probe,
4568c2ecf20Sopenharmony_ci	.resume			= atmel_classd_component_resume,
4578c2ecf20Sopenharmony_ci	.controls		= atmel_classd_snd_controls,
4588c2ecf20Sopenharmony_ci	.num_controls		= ARRAY_SIZE(atmel_classd_snd_controls),
4598c2ecf20Sopenharmony_ci	.idle_bias_on		= 1,
4608c2ecf20Sopenharmony_ci	.use_pmdown_time	= 1,
4618c2ecf20Sopenharmony_ci};
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci/* ASoC sound card */
4648c2ecf20Sopenharmony_cistatic int atmel_classd_asoc_card_init(struct device *dev,
4658c2ecf20Sopenharmony_ci					struct snd_soc_card *card)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct snd_soc_dai_link *dai_link;
4688c2ecf20Sopenharmony_ci	struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
4698c2ecf20Sopenharmony_ci	struct snd_soc_dai_link_component *comp;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
4728c2ecf20Sopenharmony_ci	if (!dai_link)
4738c2ecf20Sopenharmony_ci		return -ENOMEM;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL);
4768c2ecf20Sopenharmony_ci	if (!comp)
4778c2ecf20Sopenharmony_ci		return -ENOMEM;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	dai_link->cpus		= &comp[0];
4808c2ecf20Sopenharmony_ci	dai_link->codecs	= &comp[1];
4818c2ecf20Sopenharmony_ci	dai_link->platforms	= &comp[2];
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	dai_link->num_cpus	= 1;
4848c2ecf20Sopenharmony_ci	dai_link->num_codecs	= 1;
4858c2ecf20Sopenharmony_ci	dai_link->num_platforms	= 1;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	dai_link->name			= "CLASSD";
4888c2ecf20Sopenharmony_ci	dai_link->stream_name		= "CLASSD PCM";
4898c2ecf20Sopenharmony_ci	dai_link->codecs->dai_name	= "snd-soc-dummy-dai";
4908c2ecf20Sopenharmony_ci	dai_link->cpus->dai_name	= dev_name(dev);
4918c2ecf20Sopenharmony_ci	dai_link->codecs->name		= "snd-soc-dummy";
4928c2ecf20Sopenharmony_ci	dai_link->platforms->name	= dev_name(dev);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci	card->dai_link	= dai_link;
4958c2ecf20Sopenharmony_ci	card->num_links	= 1;
4968c2ecf20Sopenharmony_ci	card->name	= dd->pdata->card_name;
4978c2ecf20Sopenharmony_ci	card->dev	= dev;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	return 0;
5008c2ecf20Sopenharmony_ci};
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci/* regmap configuration */
5038c2ecf20Sopenharmony_cistatic const struct reg_default atmel_classd_reg_defaults[] = {
5048c2ecf20Sopenharmony_ci	{ CLASSD_INTPMR,   0x00301212 },
5058c2ecf20Sopenharmony_ci};
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci#define ATMEL_CLASSD_REG_MAX    0xE4
5088c2ecf20Sopenharmony_cistatic const struct regmap_config atmel_classd_regmap_config = {
5098c2ecf20Sopenharmony_ci	.reg_bits	= 32,
5108c2ecf20Sopenharmony_ci	.reg_stride	= 4,
5118c2ecf20Sopenharmony_ci	.val_bits	= 32,
5128c2ecf20Sopenharmony_ci	.max_register	= ATMEL_CLASSD_REG_MAX,
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	.cache_type		= REGCACHE_FLAT,
5158c2ecf20Sopenharmony_ci	.reg_defaults		= atmel_classd_reg_defaults,
5168c2ecf20Sopenharmony_ci	.num_reg_defaults	= ARRAY_SIZE(atmel_classd_reg_defaults),
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int atmel_classd_probe(struct platform_device *pdev)
5208c2ecf20Sopenharmony_ci{
5218c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
5228c2ecf20Sopenharmony_ci	struct atmel_classd *dd;
5238c2ecf20Sopenharmony_ci	struct resource *res;
5248c2ecf20Sopenharmony_ci	void __iomem *io_base;
5258c2ecf20Sopenharmony_ci	const struct atmel_classd_pdata *pdata;
5268c2ecf20Sopenharmony_ci	struct snd_soc_card *card;
5278c2ecf20Sopenharmony_ci	int ret;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	pdata = dev_get_platdata(dev);
5308c2ecf20Sopenharmony_ci	if (!pdata) {
5318c2ecf20Sopenharmony_ci		pdata = atmel_classd_dt_init(dev);
5328c2ecf20Sopenharmony_ci		if (IS_ERR(pdata))
5338c2ecf20Sopenharmony_ci			return PTR_ERR(pdata);
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
5378c2ecf20Sopenharmony_ci	if (!dd)
5388c2ecf20Sopenharmony_ci		return -ENOMEM;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	dd->pdata = pdata;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	dd->irq = platform_get_irq(pdev, 0);
5438c2ecf20Sopenharmony_ci	if (dd->irq < 0)
5448c2ecf20Sopenharmony_ci		return dd->irq;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	dd->pclk = devm_clk_get(dev, "pclk");
5478c2ecf20Sopenharmony_ci	if (IS_ERR(dd->pclk)) {
5488c2ecf20Sopenharmony_ci		ret = PTR_ERR(dd->pclk);
5498c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get peripheral clock: %d\n", ret);
5508c2ecf20Sopenharmony_ci		return ret;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	dd->gclk = devm_clk_get(dev, "gclk");
5548c2ecf20Sopenharmony_ci	if (IS_ERR(dd->gclk)) {
5558c2ecf20Sopenharmony_ci		ret = PTR_ERR(dd->gclk);
5568c2ecf20Sopenharmony_ci		dev_err(dev, "failed to get GCK clock: %d\n", ret);
5578c2ecf20Sopenharmony_ci		return ret;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
5618c2ecf20Sopenharmony_ci	io_base = devm_ioremap_resource(dev, res);
5628c2ecf20Sopenharmony_ci	if (IS_ERR(io_base))
5638c2ecf20Sopenharmony_ci		return PTR_ERR(io_base);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	dd->phy_base = res->start;
5668c2ecf20Sopenharmony_ci	dd->dev = dev;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	dd->regmap = devm_regmap_init_mmio(dev, io_base,
5698c2ecf20Sopenharmony_ci					&atmel_classd_regmap_config);
5708c2ecf20Sopenharmony_ci	if (IS_ERR(dd->regmap)) {
5718c2ecf20Sopenharmony_ci		ret = PTR_ERR(dd->regmap);
5728c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init register map: %d\n", ret);
5738c2ecf20Sopenharmony_ci		return ret;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_component(dev,
5778c2ecf20Sopenharmony_ci					&atmel_classd_cpu_dai_component,
5788c2ecf20Sopenharmony_ci					&atmel_classd_cpu_dai, 1);
5798c2ecf20Sopenharmony_ci	if (ret) {
5808c2ecf20Sopenharmony_ci		dev_err(dev, "could not register CPU DAI: %d\n", ret);
5818c2ecf20Sopenharmony_ci		return ret;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	ret = devm_snd_dmaengine_pcm_register(dev,
5858c2ecf20Sopenharmony_ci					&atmel_classd_dmaengine_pcm_config,
5868c2ecf20Sopenharmony_ci					0);
5878c2ecf20Sopenharmony_ci	if (ret) {
5888c2ecf20Sopenharmony_ci		dev_err(dev, "could not register platform: %d\n", ret);
5898c2ecf20Sopenharmony_ci		return ret;
5908c2ecf20Sopenharmony_ci	}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	/* register sound card */
5938c2ecf20Sopenharmony_ci	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
5948c2ecf20Sopenharmony_ci	if (!card) {
5958c2ecf20Sopenharmony_ci		ret = -ENOMEM;
5968c2ecf20Sopenharmony_ci		goto unregister_codec;
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	snd_soc_card_set_drvdata(card, dd);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	ret = atmel_classd_asoc_card_init(dev, card);
6028c2ecf20Sopenharmony_ci	if (ret) {
6038c2ecf20Sopenharmony_ci		dev_err(dev, "failed to init sound card\n");
6048c2ecf20Sopenharmony_ci		goto unregister_codec;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	ret = devm_snd_soc_register_card(dev, card);
6088c2ecf20Sopenharmony_ci	if (ret) {
6098c2ecf20Sopenharmony_ci		dev_err(dev, "failed to register sound card: %d\n", ret);
6108c2ecf20Sopenharmony_ci		goto unregister_codec;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return 0;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ciunregister_codec:
6168c2ecf20Sopenharmony_ci	return ret;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic int atmel_classd_remove(struct platform_device *pdev)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	return 0;
6228c2ecf20Sopenharmony_ci}
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_cistatic struct platform_driver atmel_classd_driver = {
6258c2ecf20Sopenharmony_ci	.driver	= {
6268c2ecf20Sopenharmony_ci		.name		= "atmel-classd",
6278c2ecf20Sopenharmony_ci		.of_match_table	= of_match_ptr(atmel_classd_of_match),
6288c2ecf20Sopenharmony_ci		.pm		= &snd_soc_pm_ops,
6298c2ecf20Sopenharmony_ci	},
6308c2ecf20Sopenharmony_ci	.probe	= atmel_classd_probe,
6318c2ecf20Sopenharmony_ci	.remove	= atmel_classd_remove,
6328c2ecf20Sopenharmony_ci};
6338c2ecf20Sopenharmony_cimodule_platform_driver(atmel_classd_driver);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
6368c2ecf20Sopenharmony_ciMODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
6378c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
638