18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * uda134x.c -- UDA134X ALSA SoC Codec driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Modifications by Christian Pellegrin <chripell@evolware.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2007 Dension Audio Systems Ltd. 88c2ecf20Sopenharmony_ci * Author: Zoltan Devai 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <sound/pcm.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 188c2ecf20Sopenharmony_ci#include <sound/soc.h> 198c2ecf20Sopenharmony_ci#include <sound/initval.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <sound/uda134x.h> 228c2ecf20Sopenharmony_ci#include <sound/l3.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "uda134x.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000 288c2ecf20Sopenharmony_ci#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \ 298c2ecf20Sopenharmony_ci SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct uda134x_priv { 328c2ecf20Sopenharmony_ci int sysclk; 338c2ecf20Sopenharmony_ci int dai_fmt; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci struct snd_pcm_substream *master_substream; 368c2ecf20Sopenharmony_ci struct snd_pcm_substream *slave_substream; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci struct regmap *regmap; 398c2ecf20Sopenharmony_ci struct uda134x_platform_data *pd; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic const struct reg_default uda134x_reg_defaults[] = { 438c2ecf20Sopenharmony_ci { UDA134X_EA000, 0x04 }, 448c2ecf20Sopenharmony_ci { UDA134X_EA001, 0x04 }, 458c2ecf20Sopenharmony_ci { UDA134X_EA010, 0x04 }, 468c2ecf20Sopenharmony_ci { UDA134X_EA011, 0x00 }, 478c2ecf20Sopenharmony_ci { UDA134X_EA100, 0x00 }, 488c2ecf20Sopenharmony_ci { UDA134X_EA101, 0x00 }, 498c2ecf20Sopenharmony_ci { UDA134X_EA110, 0x00 }, 508c2ecf20Sopenharmony_ci { UDA134X_EA111, 0x00 }, 518c2ecf20Sopenharmony_ci { UDA134X_STATUS0, 0x00 }, 528c2ecf20Sopenharmony_ci { UDA134X_STATUS1, 0x03 }, 538c2ecf20Sopenharmony_ci { UDA134X_DATA000, 0x00 }, 548c2ecf20Sopenharmony_ci { UDA134X_DATA001, 0x00 }, 558c2ecf20Sopenharmony_ci { UDA134X_DATA010, 0x00 }, 568c2ecf20Sopenharmony_ci { UDA134X_DATA011, 0x00 }, 578c2ecf20Sopenharmony_ci { UDA134X_DATA1, 0x00 }, 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * Write to the uda134x registers 628c2ecf20Sopenharmony_ci * 638c2ecf20Sopenharmony_ci */ 648c2ecf20Sopenharmony_cistatic int uda134x_regmap_write(void *context, unsigned int reg, 658c2ecf20Sopenharmony_ci unsigned int value) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct uda134x_platform_data *pd = context; 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci u8 addr; 708c2ecf20Sopenharmony_ci u8 data = value; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci switch (reg) { 738c2ecf20Sopenharmony_ci case UDA134X_STATUS0: 748c2ecf20Sopenharmony_ci case UDA134X_STATUS1: 758c2ecf20Sopenharmony_ci addr = UDA134X_STATUS_ADDR; 768c2ecf20Sopenharmony_ci data |= (reg - UDA134X_STATUS0) << 7; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case UDA134X_DATA000: 798c2ecf20Sopenharmony_ci case UDA134X_DATA001: 808c2ecf20Sopenharmony_ci case UDA134X_DATA010: 818c2ecf20Sopenharmony_ci case UDA134X_DATA011: 828c2ecf20Sopenharmony_ci addr = UDA134X_DATA0_ADDR; 838c2ecf20Sopenharmony_ci data |= (reg - UDA134X_DATA000) << 6; 848c2ecf20Sopenharmony_ci break; 858c2ecf20Sopenharmony_ci case UDA134X_DATA1: 868c2ecf20Sopenharmony_ci addr = UDA134X_DATA1_ADDR; 878c2ecf20Sopenharmony_ci break; 888c2ecf20Sopenharmony_ci default: 898c2ecf20Sopenharmony_ci /* It's an extended address register */ 908c2ecf20Sopenharmony_ci addr = (reg | UDA134X_EXTADDR_PREFIX); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci ret = l3_write(&pd->l3, 938c2ecf20Sopenharmony_ci UDA134X_DATA0_ADDR, &addr, 1); 948c2ecf20Sopenharmony_ci if (ret != 1) 958c2ecf20Sopenharmony_ci return -EIO; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci addr = UDA134X_DATA0_ADDR; 988c2ecf20Sopenharmony_ci data = (value | UDA134X_EXTDATA_PREFIX); 998c2ecf20Sopenharmony_ci break; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci ret = l3_write(&pd->l3, 1038c2ecf20Sopenharmony_ci addr, &data, 1); 1048c2ecf20Sopenharmony_ci if (ret != 1) 1058c2ecf20Sopenharmony_ci return -EIO; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci return 0; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic inline void uda134x_reset(struct snd_soc_component *component) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 1138c2ecf20Sopenharmony_ci unsigned int mask = 1<<6; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask); 1168c2ecf20Sopenharmony_ci msleep(1); 1178c2ecf20Sopenharmony_ci regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic int uda134x_mute(struct snd_soc_dai *dai, int mute, int direction) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component); 1238c2ecf20Sopenharmony_ci unsigned int mask = 1<<2; 1248c2ecf20Sopenharmony_ci unsigned int val; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci pr_debug("%s mute: %d\n", __func__, mute); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (mute) 1298c2ecf20Sopenharmony_ci val = mask; 1308c2ecf20Sopenharmony_ci else 1318c2ecf20Sopenharmony_ci val = 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic int uda134x_startup(struct snd_pcm_substream *substream, 1378c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1408c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 1418c2ecf20Sopenharmony_ci struct snd_pcm_runtime *master_runtime; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (uda134x->master_substream) { 1448c2ecf20Sopenharmony_ci master_runtime = uda134x->master_substream->runtime; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci pr_debug("%s constraining to %d bits at %d\n", __func__, 1478c2ecf20Sopenharmony_ci master_runtime->sample_bits, 1488c2ecf20Sopenharmony_ci master_runtime->rate); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_single(substream->runtime, 1518c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 1528c2ecf20Sopenharmony_ci master_runtime->rate); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_single(substream->runtime, 1558c2ecf20Sopenharmony_ci SNDRV_PCM_HW_PARAM_SAMPLE_BITS, 1568c2ecf20Sopenharmony_ci master_runtime->sample_bits); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci uda134x->slave_substream = substream; 1598c2ecf20Sopenharmony_ci } else 1608c2ecf20Sopenharmony_ci uda134x->master_substream = substream; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic void uda134x_shutdown(struct snd_pcm_substream *substream, 1668c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1698c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (uda134x->master_substream == substream) 1728c2ecf20Sopenharmony_ci uda134x->master_substream = uda134x->slave_substream; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci uda134x->slave_substream = NULL; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic int uda134x_hw_params(struct snd_pcm_substream *substream, 1788c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, 1798c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct snd_soc_component *component = dai->component; 1828c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 1838c2ecf20Sopenharmony_ci unsigned int hw_params = 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (substream == uda134x->slave_substream) { 1868c2ecf20Sopenharmony_ci pr_debug("%s ignoring hw_params for slave substream\n", 1878c2ecf20Sopenharmony_ci __func__); 1888c2ecf20Sopenharmony_ci return 0; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci pr_debug("%s sysclk: %d, rate:%d\n", __func__, 1928c2ecf20Sopenharmony_ci uda134x->sysclk, params_rate(params)); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* set SYSCLK / fs ratio */ 1958c2ecf20Sopenharmony_ci switch (uda134x->sysclk / params_rate(params)) { 1968c2ecf20Sopenharmony_ci case 512: 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci case 384: 1998c2ecf20Sopenharmony_ci hw_params |= (1<<4); 2008c2ecf20Sopenharmony_ci break; 2018c2ecf20Sopenharmony_ci case 256: 2028c2ecf20Sopenharmony_ci hw_params |= (1<<5); 2038c2ecf20Sopenharmony_ci break; 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unsupported fs\n", __func__); 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__, 2108c2ecf20Sopenharmony_ci uda134x->dai_fmt, params_format(params)); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* set DAI format and word length */ 2138c2ecf20Sopenharmony_ci switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 2148c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 2178c2ecf20Sopenharmony_ci switch (params_width(params)) { 2188c2ecf20Sopenharmony_ci case 16: 2198c2ecf20Sopenharmony_ci hw_params |= (1<<1); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci case 18: 2228c2ecf20Sopenharmony_ci hw_params |= (1<<2); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci case 20: 2258c2ecf20Sopenharmony_ci hw_params |= ((1<<2) | (1<<1)); 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci default: 2288c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unsupported format (right)\n", 2298c2ecf20Sopenharmony_ci __func__); 2308c2ecf20Sopenharmony_ci return -EINVAL; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 2348c2ecf20Sopenharmony_ci hw_params |= (1<<3); 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci default: 2378c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unsupported format\n", __func__); 2388c2ecf20Sopenharmony_ci return -EINVAL; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, 2428c2ecf20Sopenharmony_ci STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai, 2468c2ecf20Sopenharmony_ci int clk_id, unsigned int freq, int dir) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 2498c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__, 2528c2ecf20Sopenharmony_ci clk_id, freq, dir); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable 2558c2ecf20Sopenharmony_ci because the codec is slave. Of course limitations of the clock 2568c2ecf20Sopenharmony_ci master (the IIS controller) apply. 2578c2ecf20Sopenharmony_ci We'll error out on set_hw_params if it's not OK */ 2588c2ecf20Sopenharmony_ci if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) { 2598c2ecf20Sopenharmony_ci uda134x->sysclk = freq; 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unsupported sysclk\n", __func__); 2648c2ecf20Sopenharmony_ci return -EINVAL; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai, 2688c2ecf20Sopenharmony_ci unsigned int fmt) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 2718c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci pr_debug("%s fmt: %08X\n", __func__, fmt); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* codec supports only full slave mode */ 2768c2ecf20Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { 2778c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unsupported slave mode\n", __func__); 2788c2ecf20Sopenharmony_ci return -EINVAL; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* no support for clock inversion */ 2828c2ecf20Sopenharmony_ci if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { 2838c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unsupported clock inversion\n", __func__); 2848c2ecf20Sopenharmony_ci return -EINVAL; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* We can't setup DAI format here as it depends on the word bit num */ 2888c2ecf20Sopenharmony_ci /* so let's just store the value for later */ 2898c2ecf20Sopenharmony_ci uda134x->dai_fmt = fmt; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci return 0; 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int uda134x_set_bias_level(struct snd_soc_component *component, 2958c2ecf20Sopenharmony_ci enum snd_soc_bias_level level) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 2988c2ecf20Sopenharmony_ci struct uda134x_platform_data *pd = uda134x->pd; 2998c2ecf20Sopenharmony_ci pr_debug("%s bias level %d\n", __func__, level); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci switch (level) { 3028c2ecf20Sopenharmony_ci case SND_SOC_BIAS_ON: 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 3058c2ecf20Sopenharmony_ci /* power on */ 3068c2ecf20Sopenharmony_ci if (pd->power) { 3078c2ecf20Sopenharmony_ci pd->power(1); 3088c2ecf20Sopenharmony_ci regcache_sync(uda134x->regmap); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci break; 3118c2ecf20Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case SND_SOC_BIAS_OFF: 3148c2ecf20Sopenharmony_ci /* power off */ 3158c2ecf20Sopenharmony_ci if (pd->power) { 3168c2ecf20Sopenharmony_ci pd->power(0); 3178c2ecf20Sopenharmony_ci regcache_mark_dirty(uda134x->regmap); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic const char *uda134x_dsp_setting[] = {"Flat", "Minimum1", 3258c2ecf20Sopenharmony_ci "Minimum2", "Maximum"}; 3268c2ecf20Sopenharmony_cistatic const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"}; 3278c2ecf20Sopenharmony_cistatic const char *uda134x_mixmode[] = {"Differential", "Analog1", 3288c2ecf20Sopenharmony_ci "Analog2", "Both"}; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic const struct soc_enum uda134x_mixer_enum[] = { 3318c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting), 3328c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph), 3338c2ecf20Sopenharmony_ciSOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode), 3348c2ecf20Sopenharmony_ci}; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new uda1341_snd_controls[] = { 3378c2ecf20Sopenharmony_ciSOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), 3388c2ecf20Sopenharmony_ciSOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0), 3398c2ecf20Sopenharmony_ciSOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1), 3408c2ecf20Sopenharmony_ciSOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1), 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ciSOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0), 3438c2ecf20Sopenharmony_ciSOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0), 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ciSOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0), 3468c2ecf20Sopenharmony_ciSOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0), 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ciSOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]), 3498c2ecf20Sopenharmony_ciSOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), 3508c2ecf20Sopenharmony_ciSOC_ENUM("Input Mux", uda134x_mixer_enum[2]), 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ciSOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0), 3538c2ecf20Sopenharmony_ciSOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1), 3548c2ecf20Sopenharmony_ciSOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0), 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ciSOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0), 3578c2ecf20Sopenharmony_ciSOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0), 3588c2ecf20Sopenharmony_ciSOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0), 3598c2ecf20Sopenharmony_ciSOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0), 3608c2ecf20Sopenharmony_ciSOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0), 3618c2ecf20Sopenharmony_ciSOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new uda1340_snd_controls[] = { 3658c2ecf20Sopenharmony_ciSOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ciSOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0), 3688c2ecf20Sopenharmony_ciSOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0), 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciSOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]), 3718c2ecf20Sopenharmony_ciSOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ciSOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), 3748c2ecf20Sopenharmony_ci}; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new uda1345_snd_controls[] = { 3778c2ecf20Sopenharmony_ciSOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1), 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ciSOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]), 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ciSOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0), 3828c2ecf20Sopenharmony_ci}; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* UDA1341 has the DAC/ADC power down in STATUS1 */ 3858c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = { 3868c2ecf20Sopenharmony_ci SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0), 3878c2ecf20Sopenharmony_ci SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0), 3888c2ecf20Sopenharmony_ci}; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */ 3918c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = { 3928c2ecf20Sopenharmony_ci SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0), 3938c2ecf20Sopenharmony_ci SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0), 3948c2ecf20Sopenharmony_ci}; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci/* Common DAPM widgets */ 3978c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = { 3988c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("VINL1"), 3998c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("VINR1"), 4008c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("VINL2"), 4018c2ecf20Sopenharmony_ci SND_SOC_DAPM_INPUT("VINR2"), 4028c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("VOUTL"), 4038c2ecf20Sopenharmony_ci SND_SOC_DAPM_OUTPUT("VOUTR"), 4048c2ecf20Sopenharmony_ci}; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic const struct snd_soc_dapm_route uda134x_dapm_routes[] = { 4078c2ecf20Sopenharmony_ci { "ADC", NULL, "VINL1" }, 4088c2ecf20Sopenharmony_ci { "ADC", NULL, "VINR1" }, 4098c2ecf20Sopenharmony_ci { "ADC", NULL, "VINL2" }, 4108c2ecf20Sopenharmony_ci { "ADC", NULL, "VINR2" }, 4118c2ecf20Sopenharmony_ci { "VOUTL", NULL, "DAC" }, 4128c2ecf20Sopenharmony_ci { "VOUTR", NULL, "DAC" }, 4138c2ecf20Sopenharmony_ci}; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops uda134x_dai_ops = { 4168c2ecf20Sopenharmony_ci .startup = uda134x_startup, 4178c2ecf20Sopenharmony_ci .shutdown = uda134x_shutdown, 4188c2ecf20Sopenharmony_ci .hw_params = uda134x_hw_params, 4198c2ecf20Sopenharmony_ci .mute_stream = uda134x_mute, 4208c2ecf20Sopenharmony_ci .set_sysclk = uda134x_set_dai_sysclk, 4218c2ecf20Sopenharmony_ci .set_fmt = uda134x_set_dai_fmt, 4228c2ecf20Sopenharmony_ci .no_capture_mute = 1, 4238c2ecf20Sopenharmony_ci}; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic struct snd_soc_dai_driver uda134x_dai = { 4268c2ecf20Sopenharmony_ci .name = "uda134x-hifi", 4278c2ecf20Sopenharmony_ci /* playback capabilities */ 4288c2ecf20Sopenharmony_ci .playback = { 4298c2ecf20Sopenharmony_ci .stream_name = "Playback", 4308c2ecf20Sopenharmony_ci .channels_min = 1, 4318c2ecf20Sopenharmony_ci .channels_max = 2, 4328c2ecf20Sopenharmony_ci .rates = UDA134X_RATES, 4338c2ecf20Sopenharmony_ci .formats = UDA134X_FORMATS, 4348c2ecf20Sopenharmony_ci }, 4358c2ecf20Sopenharmony_ci /* capture capabilities */ 4368c2ecf20Sopenharmony_ci .capture = { 4378c2ecf20Sopenharmony_ci .stream_name = "Capture", 4388c2ecf20Sopenharmony_ci .channels_min = 1, 4398c2ecf20Sopenharmony_ci .channels_max = 2, 4408c2ecf20Sopenharmony_ci .rates = UDA134X_RATES, 4418c2ecf20Sopenharmony_ci .formats = UDA134X_FORMATS, 4428c2ecf20Sopenharmony_ci }, 4438c2ecf20Sopenharmony_ci /* pcm operations */ 4448c2ecf20Sopenharmony_ci .ops = &uda134x_dai_ops, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int uda134x_soc_probe(struct snd_soc_component *component) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 4508c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component); 4518c2ecf20Sopenharmony_ci struct uda134x_platform_data *pd = uda134x->pd; 4528c2ecf20Sopenharmony_ci const struct snd_soc_dapm_widget *widgets; 4538c2ecf20Sopenharmony_ci unsigned num_widgets; 4548c2ecf20Sopenharmony_ci int ret; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci printk(KERN_INFO "UDA134X SoC Audio Codec\n"); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci switch (pd->model) { 4598c2ecf20Sopenharmony_ci case UDA134X_UDA1340: 4608c2ecf20Sopenharmony_ci case UDA134X_UDA1341: 4618c2ecf20Sopenharmony_ci case UDA134X_UDA1344: 4628c2ecf20Sopenharmony_ci case UDA134X_UDA1345: 4638c2ecf20Sopenharmony_ci break; 4648c2ecf20Sopenharmony_ci default: 4658c2ecf20Sopenharmony_ci printk(KERN_ERR "UDA134X SoC codec: " 4668c2ecf20Sopenharmony_ci "unsupported model %d\n", 4678c2ecf20Sopenharmony_ci pd->model); 4688c2ecf20Sopenharmony_ci return -EINVAL; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (pd->power) 4728c2ecf20Sopenharmony_ci pd->power(1); 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci uda134x_reset(component); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (pd->model == UDA134X_UDA1341) { 4778c2ecf20Sopenharmony_ci widgets = uda1341_dapm_widgets; 4788c2ecf20Sopenharmony_ci num_widgets = ARRAY_SIZE(uda1341_dapm_widgets); 4798c2ecf20Sopenharmony_ci } else { 4808c2ecf20Sopenharmony_ci widgets = uda1340_dapm_widgets; 4818c2ecf20Sopenharmony_ci num_widgets = ARRAY_SIZE(uda1340_dapm_widgets); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets); 4858c2ecf20Sopenharmony_ci if (ret) { 4868c2ecf20Sopenharmony_ci printk(KERN_ERR "%s failed to register dapm controls: %d", 4878c2ecf20Sopenharmony_ci __func__, ret); 4888c2ecf20Sopenharmony_ci return ret; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci switch (pd->model) { 4928c2ecf20Sopenharmony_ci case UDA134X_UDA1340: 4938c2ecf20Sopenharmony_ci case UDA134X_UDA1344: 4948c2ecf20Sopenharmony_ci ret = snd_soc_add_component_controls(component, uda1340_snd_controls, 4958c2ecf20Sopenharmony_ci ARRAY_SIZE(uda1340_snd_controls)); 4968c2ecf20Sopenharmony_ci break; 4978c2ecf20Sopenharmony_ci case UDA134X_UDA1341: 4988c2ecf20Sopenharmony_ci ret = snd_soc_add_component_controls(component, uda1341_snd_controls, 4998c2ecf20Sopenharmony_ci ARRAY_SIZE(uda1341_snd_controls)); 5008c2ecf20Sopenharmony_ci break; 5018c2ecf20Sopenharmony_ci case UDA134X_UDA1345: 5028c2ecf20Sopenharmony_ci ret = snd_soc_add_component_controls(component, uda1345_snd_controls, 5038c2ecf20Sopenharmony_ci ARRAY_SIZE(uda1345_snd_controls)); 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci default: 5068c2ecf20Sopenharmony_ci printk(KERN_ERR "%s unknown codec type: %d", 5078c2ecf20Sopenharmony_ci __func__, pd->model); 5088c2ecf20Sopenharmony_ci return -EINVAL; 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (ret < 0) { 5128c2ecf20Sopenharmony_ci printk(KERN_ERR "UDA134X: failed to register controls\n"); 5138c2ecf20Sopenharmony_ci return ret; 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci return 0; 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_uda134x = { 5208c2ecf20Sopenharmony_ci .probe = uda134x_soc_probe, 5218c2ecf20Sopenharmony_ci .set_bias_level = uda134x_set_bias_level, 5228c2ecf20Sopenharmony_ci .dapm_widgets = uda134x_dapm_widgets, 5238c2ecf20Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets), 5248c2ecf20Sopenharmony_ci .dapm_routes = uda134x_dapm_routes, 5258c2ecf20Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes), 5268c2ecf20Sopenharmony_ci .suspend_bias_off = 1, 5278c2ecf20Sopenharmony_ci .idle_bias_on = 1, 5288c2ecf20Sopenharmony_ci .use_pmdown_time = 1, 5298c2ecf20Sopenharmony_ci .endianness = 1, 5308c2ecf20Sopenharmony_ci .non_legacy_dai_naming = 1, 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic const struct regmap_config uda134x_regmap_config = { 5348c2ecf20Sopenharmony_ci .reg_bits = 8, 5358c2ecf20Sopenharmony_ci .val_bits = 8, 5368c2ecf20Sopenharmony_ci .max_register = UDA134X_DATA1, 5378c2ecf20Sopenharmony_ci .reg_defaults = uda134x_reg_defaults, 5388c2ecf20Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults), 5398c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci .reg_write = uda134x_regmap_write, 5428c2ecf20Sopenharmony_ci}; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int uda134x_codec_probe(struct platform_device *pdev) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci struct uda134x_platform_data *pd = pdev->dev.platform_data; 5478c2ecf20Sopenharmony_ci struct uda134x_priv *uda134x; 5488c2ecf20Sopenharmony_ci int ret; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!pd) { 5518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Missing L3 bitbang function\n"); 5528c2ecf20Sopenharmony_ci return -ENODEV; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL); 5568c2ecf20Sopenharmony_ci if (!uda134x) 5578c2ecf20Sopenharmony_ci return -ENOMEM; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci uda134x->pd = pd; 5608c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, uda134x); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (pd->l3.use_gpios) { 5638c2ecf20Sopenharmony_ci ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3); 5648c2ecf20Sopenharmony_ci if (ret < 0) 5658c2ecf20Sopenharmony_ci return ret; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd, 5698c2ecf20Sopenharmony_ci &uda134x_regmap_config); 5708c2ecf20Sopenharmony_ci if (IS_ERR(uda134x->regmap)) 5718c2ecf20Sopenharmony_ci return PTR_ERR(uda134x->regmap); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci return devm_snd_soc_register_component(&pdev->dev, 5748c2ecf20Sopenharmony_ci &soc_component_dev_uda134x, &uda134x_dai, 1); 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic struct platform_driver uda134x_codec_driver = { 5788c2ecf20Sopenharmony_ci .driver = { 5798c2ecf20Sopenharmony_ci .name = "uda134x-codec", 5808c2ecf20Sopenharmony_ci }, 5818c2ecf20Sopenharmony_ci .probe = uda134x_codec_probe, 5828c2ecf20Sopenharmony_ci}; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cimodule_platform_driver(uda134x_codec_driver); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("UDA134X ALSA soc codec driver"); 5878c2ecf20Sopenharmony_ciMODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>"); 5888c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 589