18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/of.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 148c2ecf20Sopenharmony_ci#include <sound/pcm.h> 158c2ecf20Sopenharmony_ci#include <sound/pcm_params.h> 168c2ecf20Sopenharmony_ci#include <linux/regmap.h> 178c2ecf20Sopenharmony_ci#include <sound/soc.h> 188c2ecf20Sopenharmony_ci#include <sound/soc-dai.h> 198c2ecf20Sopenharmony_ci#include "lpass-lpaif-reg.h" 208c2ecf20Sopenharmony_ci#include "lpass.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define LPASS_CPU_MAX_MI2S_LINES 4 238c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD0_MASK BIT(0) 248c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD1_MASK BIT(1) 258c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD2_MASK BIT(2) 268c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD3_MASK BIT(3) 278c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD0_1_MASK GENMASK(1, 0) 288c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD2_3_MASK GENMASK(3, 2) 298c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0) 308c2ecf20Sopenharmony_ci#define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int lpass_cpu_init_i2sctl_bitfields(struct device *dev, 338c2ecf20Sopenharmony_ci struct lpaif_i2sctl *i2sctl, struct regmap *map) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 368c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback); 398c2ecf20Sopenharmony_ci i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken); 408c2ecf20Sopenharmony_ci i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode); 418c2ecf20Sopenharmony_ci i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono); 428c2ecf20Sopenharmony_ci i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen); 438c2ecf20Sopenharmony_ci i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode); 448c2ecf20Sopenharmony_ci i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono); 458c2ecf20Sopenharmony_ci i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc); 468c2ecf20Sopenharmony_ci i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) || 498c2ecf20Sopenharmony_ci IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) || 508c2ecf20Sopenharmony_ci IS_ERR(i2sctl->micen) || IS_ERR(i2sctl->micmode) || 518c2ecf20Sopenharmony_ci IS_ERR(i2sctl->micmono) || IS_ERR(i2sctl->wssrc) || 528c2ecf20Sopenharmony_ci IS_ERR(i2sctl->bitwidth)) 538c2ecf20Sopenharmony_ci return -EINVAL; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, 598c2ecf20Sopenharmony_ci unsigned int freq, int dir) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 628c2ecf20Sopenharmony_ci int ret; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq); 658c2ecf20Sopenharmony_ci if (ret) 668c2ecf20Sopenharmony_ci dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n", 678c2ecf20Sopenharmony_ci freq, ret); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return ret; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, 738c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 768c2ecf20Sopenharmony_ci int ret; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]); 798c2ecf20Sopenharmony_ci if (ret) { 808c2ecf20Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret); 818c2ecf20Sopenharmony_ci return ret; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]); 848c2ecf20Sopenharmony_ci if (ret) { 858c2ecf20Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 868c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, 938c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 968c2ecf20Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 978c2ecf20Sopenharmony_ci unsigned int id = dai->driver->id; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * Ensure LRCLK is disabled even in device node validation. 1028c2ecf20Sopenharmony_ci * Will not impact if disabled in lpass_cpu_daiops_trigger() 1038c2ecf20Sopenharmony_ci * suspend. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1068c2ecf20Sopenharmony_ci regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE); 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci /* 1118c2ecf20Sopenharmony_ci * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before 1128c2ecf20Sopenharmony_ci * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in 1138c2ecf20Sopenharmony_ci * lpass_cpu_daiops_prepare. 1148c2ecf20Sopenharmony_ci */ 1158c2ecf20Sopenharmony_ci if (drvdata->mi2s_was_prepared[dai->driver->id]) { 1168c2ecf20Sopenharmony_ci drvdata->mi2s_was_prepared[dai->driver->id] = false; 1178c2ecf20Sopenharmony_ci clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, 1248c2ecf20Sopenharmony_ci struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 1278c2ecf20Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 1288c2ecf20Sopenharmony_ci unsigned int id = dai->driver->id; 1298c2ecf20Sopenharmony_ci snd_pcm_format_t format = params_format(params); 1308c2ecf20Sopenharmony_ci unsigned int channels = params_channels(params); 1318c2ecf20Sopenharmony_ci unsigned int rate = params_rate(params); 1328c2ecf20Sopenharmony_ci unsigned int mode; 1338c2ecf20Sopenharmony_ci unsigned int regval; 1348c2ecf20Sopenharmony_ci int bitwidth, ret; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci bitwidth = snd_pcm_format_width(format); 1378c2ecf20Sopenharmony_ci if (bitwidth < 0) { 1388c2ecf20Sopenharmony_ci dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth); 1398c2ecf20Sopenharmony_ci return bitwidth; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->loopback, id, 1438c2ecf20Sopenharmony_ci LPAIF_I2SCTL_LOOPBACK_DISABLE); 1448c2ecf20Sopenharmony_ci if (ret) { 1458c2ecf20Sopenharmony_ci dev_err(dai->dev, "error updating loopback field: %d\n", ret); 1468c2ecf20Sopenharmony_ci return ret; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->wssrc, id, 1508c2ecf20Sopenharmony_ci LPAIF_I2SCTL_WSSRC_INTERNAL); 1518c2ecf20Sopenharmony_ci if (ret) { 1528c2ecf20Sopenharmony_ci dev_err(dai->dev, "error updating wssrc field: %d\n", ret); 1538c2ecf20Sopenharmony_ci return ret; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci switch (bitwidth) { 1578c2ecf20Sopenharmony_ci case 16: 1588c2ecf20Sopenharmony_ci regval = LPAIF_I2SCTL_BITWIDTH_16; 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci case 24: 1618c2ecf20Sopenharmony_ci regval = LPAIF_I2SCTL_BITWIDTH_24; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case 32: 1648c2ecf20Sopenharmony_ci regval = LPAIF_I2SCTL_BITWIDTH_32; 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci default: 1678c2ecf20Sopenharmony_ci dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth); 1688c2ecf20Sopenharmony_ci return -EINVAL; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->bitwidth, id, regval); 1728c2ecf20Sopenharmony_ci if (ret) { 1738c2ecf20Sopenharmony_ci dev_err(dai->dev, "error updating bitwidth field: %d\n", ret); 1748c2ecf20Sopenharmony_ci return ret; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1788c2ecf20Sopenharmony_ci mode = drvdata->mi2s_playback_sd_mode[id]; 1798c2ecf20Sopenharmony_ci else 1808c2ecf20Sopenharmony_ci mode = drvdata->mi2s_capture_sd_mode[id]; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (!mode) { 1838c2ecf20Sopenharmony_ci dev_err(dai->dev, "no line is assigned\n"); 1848c2ecf20Sopenharmony_ci return -EINVAL; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci switch (channels) { 1888c2ecf20Sopenharmony_ci case 1: 1898c2ecf20Sopenharmony_ci case 2: 1908c2ecf20Sopenharmony_ci switch (mode) { 1918c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_QUAD01: 1928c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_6CH: 1938c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_8CH: 1948c2ecf20Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_SD0; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_QUAD23: 1978c2ecf20Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_SD2; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case 4: 2038c2ecf20Sopenharmony_ci if (mode < LPAIF_I2SCTL_MODE_QUAD01) { 2048c2ecf20Sopenharmony_ci dev_err(dai->dev, "cannot configure 4 channels with mode %d\n", 2058c2ecf20Sopenharmony_ci mode); 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (mode) { 2108c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_6CH: 2118c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_8CH: 2128c2ecf20Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_QUAD01; 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci break; 2168c2ecf20Sopenharmony_ci case 6: 2178c2ecf20Sopenharmony_ci if (mode < LPAIF_I2SCTL_MODE_6CH) { 2188c2ecf20Sopenharmony_ci dev_err(dai->dev, "cannot configure 6 channels with mode %d\n", 2198c2ecf20Sopenharmony_ci mode); 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci switch (mode) { 2248c2ecf20Sopenharmony_ci case LPAIF_I2SCTL_MODE_8CH: 2258c2ecf20Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_6CH; 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci break; 2298c2ecf20Sopenharmony_ci case 8: 2308c2ecf20Sopenharmony_ci if (mode < LPAIF_I2SCTL_MODE_8CH) { 2318c2ecf20Sopenharmony_ci dev_err(dai->dev, "cannot configure 8 channels with mode %d\n", 2328c2ecf20Sopenharmony_ci mode); 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci default: 2378c2ecf20Sopenharmony_ci dev_err(dai->dev, "invalid channels given: %u\n", channels); 2388c2ecf20Sopenharmony_ci return -EINVAL; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 2428c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->spkmode, id, 2438c2ecf20Sopenharmony_ci LPAIF_I2SCTL_SPKMODE(mode)); 2448c2ecf20Sopenharmony_ci if (ret) { 2458c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n", 2468c2ecf20Sopenharmony_ci ret); 2478c2ecf20Sopenharmony_ci return ret; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci if (channels >= 2) 2508c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->spkmono, id, 2518c2ecf20Sopenharmony_ci LPAIF_I2SCTL_SPKMONO_STEREO); 2528c2ecf20Sopenharmony_ci else 2538c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->spkmono, id, 2548c2ecf20Sopenharmony_ci LPAIF_I2SCTL_SPKMONO_MONO); 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->micmode, id, 2578c2ecf20Sopenharmony_ci LPAIF_I2SCTL_MICMODE(mode)); 2588c2ecf20Sopenharmony_ci if (ret) { 2598c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n", 2608c2ecf20Sopenharmony_ci ret); 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci if (channels >= 2) 2648c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->micmono, id, 2658c2ecf20Sopenharmony_ci LPAIF_I2SCTL_MICMONO_STEREO); 2668c2ecf20Sopenharmony_ci else 2678c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->micmono, id, 2688c2ecf20Sopenharmony_ci LPAIF_I2SCTL_MICMONO_MONO); 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (ret) { 2728c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n", 2738c2ecf20Sopenharmony_ci ret); 2748c2ecf20Sopenharmony_ci return ret; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci ret = clk_set_rate(drvdata->mi2s_bit_clk[id], 2788c2ecf20Sopenharmony_ci rate * bitwidth * 2); 2798c2ecf20Sopenharmony_ci if (ret) { 2808c2ecf20Sopenharmony_ci dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n", 2818c2ecf20Sopenharmony_ci rate * bitwidth * 2, ret); 2828c2ecf20Sopenharmony_ci return ret; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci return 0; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, 2898c2ecf20Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 2928c2ecf20Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 2938c2ecf20Sopenharmony_ci unsigned int id = dai->driver->id; 2948c2ecf20Sopenharmony_ci int ret = -EINVAL; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci switch (cmd) { 2978c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2988c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 2998c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 3008c2ecf20Sopenharmony_ci /* 3018c2ecf20Sopenharmony_ci * Ensure lpass BCLK/LRCLK is enabled during 3028c2ecf20Sopenharmony_ci * device resume as lpass_cpu_daiops_prepare() is not called 3038c2ecf20Sopenharmony_ci * after the device resumes. We don't check mi2s_was_prepared before 3048c2ecf20Sopenharmony_ci * enable/disable BCLK in trigger events because: 3058c2ecf20Sopenharmony_ci * 1. These trigger events are paired, so the BCLK 3068c2ecf20Sopenharmony_ci * enable_count is balanced. 3078c2ecf20Sopenharmony_ci * 2. the BCLK can be shared (ex: headset and headset mic), 3088c2ecf20Sopenharmony_ci * we need to increase the enable_count so that we don't 3098c2ecf20Sopenharmony_ci * turn off the shared BCLK while other devices are using 3108c2ecf20Sopenharmony_ci * it. 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3138c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->spken, id, 3148c2ecf20Sopenharmony_ci LPAIF_I2SCTL_SPKEN_ENABLE); 3158c2ecf20Sopenharmony_ci } else { 3168c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->micen, id, 3178c2ecf20Sopenharmony_ci LPAIF_I2SCTL_MICEN_ENABLE); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci if (ret) 3208c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", 3218c2ecf20Sopenharmony_ci ret); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ret = clk_enable(drvdata->mi2s_bit_clk[id]); 3248c2ecf20Sopenharmony_ci if (ret) { 3258c2ecf20Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 3268c2ecf20Sopenharmony_ci clk_disable(drvdata->mi2s_osr_clk[id]); 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 3318c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 3328c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 3338c2ecf20Sopenharmony_ci /* 3348c2ecf20Sopenharmony_ci * To ensure lpass BCLK/LRCLK is disabled during 3358c2ecf20Sopenharmony_ci * device suspend. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 3388c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->spken, id, 3398c2ecf20Sopenharmony_ci LPAIF_I2SCTL_SPKEN_DISABLE); 3408c2ecf20Sopenharmony_ci } else { 3418c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->micen, id, 3428c2ecf20Sopenharmony_ci LPAIF_I2SCTL_MICEN_DISABLE); 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci if (ret) 3458c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", 3468c2ecf20Sopenharmony_ci ret); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, 3578c2ecf20Sopenharmony_ci struct snd_soc_dai *dai) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 3608c2ecf20Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 3618c2ecf20Sopenharmony_ci unsigned int id = dai->driver->id; 3628c2ecf20Sopenharmony_ci int ret; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* 3658c2ecf20Sopenharmony_ci * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture 3668c2ecf20Sopenharmony_ci * data flow starts. This allows other codec to have some delay before 3678c2ecf20Sopenharmony_ci * the data flow. 3688c2ecf20Sopenharmony_ci * (ex: to drop start up pop noise before capture starts). 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 3718c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE); 3728c2ecf20Sopenharmony_ci else 3738c2ecf20Sopenharmony_ci ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (ret) { 3768c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); 3778c2ecf20Sopenharmony_ci return ret; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can 3828c2ecf20Sopenharmony_ci * be called multiple times. It's paired with the clk_disable in 3838c2ecf20Sopenharmony_ci * lpass_cpu_daiops_shutdown. 3848c2ecf20Sopenharmony_ci */ 3858c2ecf20Sopenharmony_ci if (!drvdata->mi2s_was_prepared[dai->driver->id]) { 3868c2ecf20Sopenharmony_ci ret = clk_enable(drvdata->mi2s_bit_clk[id]); 3878c2ecf20Sopenharmony_ci if (ret) { 3888c2ecf20Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 3898c2ecf20Sopenharmony_ci return ret; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci drvdata->mi2s_was_prepared[dai->driver->id] = true; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci return 0; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciconst struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { 3978c2ecf20Sopenharmony_ci .set_sysclk = lpass_cpu_daiops_set_sysclk, 3988c2ecf20Sopenharmony_ci .startup = lpass_cpu_daiops_startup, 3998c2ecf20Sopenharmony_ci .shutdown = lpass_cpu_daiops_shutdown, 4008c2ecf20Sopenharmony_ci .hw_params = lpass_cpu_daiops_hw_params, 4018c2ecf20Sopenharmony_ci .trigger = lpass_cpu_daiops_trigger, 4028c2ecf20Sopenharmony_ci .prepare = lpass_cpu_daiops_prepare, 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ciint asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 4098c2ecf20Sopenharmony_ci int ret; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* ensure audio hardware is disabled */ 4128c2ecf20Sopenharmony_ci ret = regmap_write(drvdata->lpaif_map, 4138c2ecf20Sopenharmony_ci LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0); 4148c2ecf20Sopenharmony_ci if (ret) 4158c2ecf20Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, 4228c2ecf20Sopenharmony_ci struct of_phandle_args *args, 4238c2ecf20Sopenharmony_ci const char **dai_name) 4248c2ecf20Sopenharmony_ci{ 4258c2ecf20Sopenharmony_ci struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); 4268c2ecf20Sopenharmony_ci struct lpass_variant *variant = drvdata->variant; 4278c2ecf20Sopenharmony_ci int id = args->args[0]; 4288c2ecf20Sopenharmony_ci int ret = -EINVAL; 4298c2ecf20Sopenharmony_ci int i; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci for (i = 0; i < variant->num_dai; i++) { 4328c2ecf20Sopenharmony_ci if (variant->dai_driver[i].id == id) { 4338c2ecf20Sopenharmony_ci *dai_name = variant->dai_driver[i].name; 4348c2ecf20Sopenharmony_ci ret = 0; 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return ret; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic const struct snd_soc_component_driver lpass_cpu_comp_driver = { 4438c2ecf20Sopenharmony_ci .name = "lpass-cpu", 4448c2ecf20Sopenharmony_ci .of_xlate_dai_name = asoc_qcom_of_xlate_dai_name, 4458c2ecf20Sopenharmony_ci}; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 4508c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 4518c2ecf20Sopenharmony_ci int i; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci for (i = 0; i < v->i2s_ports; ++i) 4548c2ecf20Sopenharmony_ci if (reg == LPAIF_I2SCTL_REG(v, i)) 4558c2ecf20Sopenharmony_ci return true; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < v->irq_ports; ++i) { 4588c2ecf20Sopenharmony_ci if (reg == LPAIF_IRQEN_REG(v, i)) 4598c2ecf20Sopenharmony_ci return true; 4608c2ecf20Sopenharmony_ci if (reg == LPAIF_IRQCLEAR_REG(v, i)) 4618c2ecf20Sopenharmony_ci return true; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = 0; i < v->rdma_channels; ++i) { 4658c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMACTL_REG(v, i)) 4668c2ecf20Sopenharmony_ci return true; 4678c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMABASE_REG(v, i)) 4688c2ecf20Sopenharmony_ci return true; 4698c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMABUFF_REG(v, i)) 4708c2ecf20Sopenharmony_ci return true; 4718c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMAPER_REG(v, i)) 4728c2ecf20Sopenharmony_ci return true; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci for (i = 0; i < v->wrdma_channels; ++i) { 4768c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) 4778c2ecf20Sopenharmony_ci return true; 4788c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) 4798c2ecf20Sopenharmony_ci return true; 4808c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) 4818c2ecf20Sopenharmony_ci return true; 4828c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) 4838c2ecf20Sopenharmony_ci return true; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return false; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 4928c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 4938c2ecf20Sopenharmony_ci int i; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci for (i = 0; i < v->i2s_ports; ++i) 4968c2ecf20Sopenharmony_ci if (reg == LPAIF_I2SCTL_REG(v, i)) 4978c2ecf20Sopenharmony_ci return true; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci for (i = 0; i < v->irq_ports; ++i) { 5008c2ecf20Sopenharmony_ci if (reg == LPAIF_IRQEN_REG(v, i)) 5018c2ecf20Sopenharmony_ci return true; 5028c2ecf20Sopenharmony_ci if (reg == LPAIF_IRQSTAT_REG(v, i)) 5038c2ecf20Sopenharmony_ci return true; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci for (i = 0; i < v->rdma_channels; ++i) { 5078c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMACTL_REG(v, i)) 5088c2ecf20Sopenharmony_ci return true; 5098c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMABASE_REG(v, i)) 5108c2ecf20Sopenharmony_ci return true; 5118c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMABUFF_REG(v, i)) 5128c2ecf20Sopenharmony_ci return true; 5138c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMACURR_REG(v, i)) 5148c2ecf20Sopenharmony_ci return true; 5158c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMAPER_REG(v, i)) 5168c2ecf20Sopenharmony_ci return true; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci for (i = 0; i < v->wrdma_channels; ++i) { 5208c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) 5218c2ecf20Sopenharmony_ci return true; 5228c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) 5238c2ecf20Sopenharmony_ci return true; 5248c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) 5258c2ecf20Sopenharmony_ci return true; 5268c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) 5278c2ecf20Sopenharmony_ci return true; 5288c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) 5298c2ecf20Sopenharmony_ci return true; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return false; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 5388c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 5398c2ecf20Sopenharmony_ci int i; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci for (i = 0; i < v->irq_ports; ++i) 5428c2ecf20Sopenharmony_ci if (reg == LPAIF_IRQSTAT_REG(v, i)) 5438c2ecf20Sopenharmony_ci return true; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci for (i = 0; i < v->rdma_channels; ++i) 5468c2ecf20Sopenharmony_ci if (reg == LPAIF_RDMACURR_REG(v, i)) 5478c2ecf20Sopenharmony_ci return true; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci for (i = 0; i < v->wrdma_channels; ++i) 5508c2ecf20Sopenharmony_ci if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) 5518c2ecf20Sopenharmony_ci return true; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return false; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic struct regmap_config lpass_cpu_regmap_config = { 5578c2ecf20Sopenharmony_ci .reg_bits = 32, 5588c2ecf20Sopenharmony_ci .reg_stride = 4, 5598c2ecf20Sopenharmony_ci .val_bits = 32, 5608c2ecf20Sopenharmony_ci .writeable_reg = lpass_cpu_regmap_writeable, 5618c2ecf20Sopenharmony_ci .readable_reg = lpass_cpu_regmap_readable, 5628c2ecf20Sopenharmony_ci .volatile_reg = lpass_cpu_regmap_volatile, 5638c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 5648c2ecf20Sopenharmony_ci}; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 5698c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 5708c2ecf20Sopenharmony_ci unsigned int i; 5718c2ecf20Sopenharmony_ci struct lpass_hdmi_tx_ctl *tx_ctl; 5728c2ecf20Sopenharmony_ci struct regmap_field *legacy_en; 5738c2ecf20Sopenharmony_ci struct lpass_vbit_ctrl *vbit_ctl; 5748c2ecf20Sopenharmony_ci struct regmap_field *tx_parity; 5758c2ecf20Sopenharmony_ci struct lpass_dp_metadata_ctl *meta_ctl; 5768c2ecf20Sopenharmony_ci struct lpass_sstream_ctl *sstream_ctl; 5778c2ecf20Sopenharmony_ci struct regmap_field *ch_msb; 5788c2ecf20Sopenharmony_ci struct regmap_field *ch_lsb; 5798c2ecf20Sopenharmony_ci struct lpass_hdmitx_dmactl *tx_dmactl; 5808c2ecf20Sopenharmony_ci int rval; 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL); 5838c2ecf20Sopenharmony_ci if (!tx_ctl) 5848c2ecf20Sopenharmony_ci return -ENOMEM; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset); 5878c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset); 5888c2ecf20Sopenharmony_ci drvdata->tx_ctl = tx_ctl; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en); 5918c2ecf20Sopenharmony_ci drvdata->hdmitx_legacy_en = legacy_en; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL); 5948c2ecf20Sopenharmony_ci if (!vbit_ctl) 5958c2ecf20Sopenharmony_ci return -ENOMEM; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit); 5988c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream); 5998c2ecf20Sopenharmony_ci drvdata->vbit_ctl = vbit_ctl; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity); 6038c2ecf20Sopenharmony_ci drvdata->hdmitx_parity_calc_en = tx_parity; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL); 6068c2ecf20Sopenharmony_ci if (!meta_ctl) 6078c2ecf20Sopenharmony_ci return -ENOMEM; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7); 6108c2ecf20Sopenharmony_ci if (rval) 6118c2ecf20Sopenharmony_ci return rval; 6128c2ecf20Sopenharmony_ci drvdata->meta_ctl = meta_ctl; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL); 6158c2ecf20Sopenharmony_ci if (!sstream_ctl) 6168c2ecf20Sopenharmony_ci return -ENOMEM; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9); 6198c2ecf20Sopenharmony_ci if (rval) 6208c2ecf20Sopenharmony_ci return rval; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci drvdata->sstream_ctl = sstream_ctl; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) { 6258c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb); 6268c2ecf20Sopenharmony_ci drvdata->hdmitx_ch_msb[i] = ch_msb; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb); 6298c2ecf20Sopenharmony_ci drvdata->hdmitx_ch_lsb[i] = ch_lsb; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL); 6328c2ecf20Sopenharmony_ci if (!tx_dmactl) 6338c2ecf20Sopenharmony_ci return -ENOMEM; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs); 6368c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr); 6378c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel); 6388c2ecf20Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel); 6398c2ecf20Sopenharmony_ci drvdata->hdmi_tx_dmactl[i] = tx_dmactl; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci return 0; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_cistatic bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg) 6458c2ecf20Sopenharmony_ci{ 6468c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 6478c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 6488c2ecf20Sopenharmony_ci int i; 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) 6518c2ecf20Sopenharmony_ci return true; 6528c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) 6538c2ecf20Sopenharmony_ci return true; 6548c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 6558c2ecf20Sopenharmony_ci return true; 6568c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 6578c2ecf20Sopenharmony_ci return true; 6588c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_DP_ADDR(v)) 6598c2ecf20Sopenharmony_ci return true; 6608c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) 6618c2ecf20Sopenharmony_ci return true; 6628c2ecf20Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) 6638c2ecf20Sopenharmony_ci return true; 6648c2ecf20Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v)) 6658c2ecf20Sopenharmony_ci return true; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; i++) { 6688c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) 6698c2ecf20Sopenharmony_ci return true; 6708c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) 6718c2ecf20Sopenharmony_ci return true; 6728c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) 6738c2ecf20Sopenharmony_ci return true; 6748c2ecf20Sopenharmony_ci } 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; ++i) { 6778c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) 6788c2ecf20Sopenharmony_ci return true; 6798c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) 6808c2ecf20Sopenharmony_ci return true; 6818c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) 6828c2ecf20Sopenharmony_ci return true; 6838c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) 6848c2ecf20Sopenharmony_ci return true; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci return false; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_cistatic bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 6928c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 6938c2ecf20Sopenharmony_ci int i; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) 6968c2ecf20Sopenharmony_ci return true; 6978c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) 6988c2ecf20Sopenharmony_ci return true; 6998c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 7008c2ecf20Sopenharmony_ci return true; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; i++) { 7038c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) 7048c2ecf20Sopenharmony_ci return true; 7058c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) 7068c2ecf20Sopenharmony_ci return true; 7078c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) 7088c2ecf20Sopenharmony_ci return true; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 7128c2ecf20Sopenharmony_ci return true; 7138c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_DP_ADDR(v)) 7148c2ecf20Sopenharmony_ci return true; 7158c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) 7168c2ecf20Sopenharmony_ci return true; 7178c2ecf20Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) 7188c2ecf20Sopenharmony_ci return true; 7198c2ecf20Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) 7208c2ecf20Sopenharmony_ci return true; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; ++i) { 7238c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) 7248c2ecf20Sopenharmony_ci return true; 7258c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) 7268c2ecf20Sopenharmony_ci return true; 7278c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) 7288c2ecf20Sopenharmony_ci return true; 7298c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) 7308c2ecf20Sopenharmony_ci return true; 7318c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) 7328c2ecf20Sopenharmony_ci return true; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci return false; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 7418c2ecf20Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 7428c2ecf20Sopenharmony_ci int i; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) 7458c2ecf20Sopenharmony_ci return true; 7468c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) 7478c2ecf20Sopenharmony_ci return true; 7488c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 7498c2ecf20Sopenharmony_ci return true; 7508c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 7518c2ecf20Sopenharmony_ci return true; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; ++i) { 7548c2ecf20Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) 7558c2ecf20Sopenharmony_ci return true; 7568c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) 7578c2ecf20Sopenharmony_ci return true; 7588c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) 7598c2ecf20Sopenharmony_ci return true; 7608c2ecf20Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) 7618c2ecf20Sopenharmony_ci return true; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci return false; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistruct regmap_config lpass_hdmi_regmap_config = { 7678c2ecf20Sopenharmony_ci .reg_bits = 32, 7688c2ecf20Sopenharmony_ci .reg_stride = 4, 7698c2ecf20Sopenharmony_ci .val_bits = 32, 7708c2ecf20Sopenharmony_ci .writeable_reg = lpass_hdmi_regmap_writeable, 7718c2ecf20Sopenharmony_ci .readable_reg = lpass_hdmi_regmap_readable, 7728c2ecf20Sopenharmony_ci .volatile_reg = lpass_hdmi_regmap_volatile, 7738c2ecf20Sopenharmony_ci .cache_type = REGCACHE_FLAT, 7748c2ecf20Sopenharmony_ci}; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, 7778c2ecf20Sopenharmony_ci struct device_node *node, 7788c2ecf20Sopenharmony_ci const char *name) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci unsigned int lines[LPASS_CPU_MAX_MI2S_LINES]; 7818c2ecf20Sopenharmony_ci unsigned int sd_line_mask = 0; 7828c2ecf20Sopenharmony_ci int num_lines, i; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci num_lines = of_property_read_variable_u32_array(node, name, lines, 0, 7858c2ecf20Sopenharmony_ci LPASS_CPU_MAX_MI2S_LINES); 7868c2ecf20Sopenharmony_ci if (num_lines < 0) 7878c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_NONE; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci for (i = 0; i < num_lines; i++) 7908c2ecf20Sopenharmony_ci sd_line_mask |= BIT(lines[i]); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci switch (sd_line_mask) { 7938c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD0_MASK: 7948c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD0; 7958c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD1_MASK: 7968c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD1; 7978c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD2_MASK: 7988c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD2; 7998c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD3_MASK: 8008c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD3; 8018c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD0_1_MASK: 8028c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_QUAD01; 8038c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD2_3_MASK: 8048c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_QUAD23; 8058c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD0_1_2_MASK: 8068c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_6CH; 8078c2ecf20Sopenharmony_ci case LPASS_CPU_I2S_SD0_1_2_3_MASK: 8088c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_8CH; 8098c2ecf20Sopenharmony_ci default: 8108c2ecf20Sopenharmony_ci dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask); 8118c2ecf20Sopenharmony_ci return LPAIF_I2SCTL_MODE_NONE; 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci} 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic void of_lpass_cpu_parse_dai_data(struct device *dev, 8168c2ecf20Sopenharmony_ci struct lpass_data *data) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci struct device_node *node; 8198c2ecf20Sopenharmony_ci int ret, i, id; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* Allow all channels by default for backwards compatibility */ 8228c2ecf20Sopenharmony_ci for (i = 0; i < data->variant->num_dai; i++) { 8238c2ecf20Sopenharmony_ci id = data->variant->dai_driver[i].id; 8248c2ecf20Sopenharmony_ci data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; 8258c2ecf20Sopenharmony_ci data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci for_each_child_of_node(dev->of_node, node) { 8298c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "reg", &id); 8308c2ecf20Sopenharmony_ci if (ret || id < 0) { 8318c2ecf20Sopenharmony_ci dev_err(dev, "valid dai id not found: %d\n", ret); 8328c2ecf20Sopenharmony_ci continue; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci if (id == LPASS_DP_RX) { 8358c2ecf20Sopenharmony_ci data->hdmi_port_enable = 1; 8368c2ecf20Sopenharmony_ci } else { 8378c2ecf20Sopenharmony_ci data->mi2s_playback_sd_mode[id] = 8388c2ecf20Sopenharmony_ci of_lpass_cpu_parse_sd_lines(dev, node, 8398c2ecf20Sopenharmony_ci "qcom,playback-sd-lines"); 8408c2ecf20Sopenharmony_ci data->mi2s_capture_sd_mode[id] = 8418c2ecf20Sopenharmony_ci of_lpass_cpu_parse_sd_lines(dev, node, 8428c2ecf20Sopenharmony_ci "qcom,capture-sd-lines"); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci} 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ciint asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci struct lpass_data *drvdata; 8508c2ecf20Sopenharmony_ci struct device_node *dsp_of_node; 8518c2ecf20Sopenharmony_ci struct resource *res; 8528c2ecf20Sopenharmony_ci struct lpass_variant *variant; 8538c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 8548c2ecf20Sopenharmony_ci const struct of_device_id *match; 8558c2ecf20Sopenharmony_ci int ret, i, dai_id; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); 8588c2ecf20Sopenharmony_ci if (dsp_of_node) { 8598c2ecf20Sopenharmony_ci dev_err(dev, "DSP exists and holds audio resources\n"); 8608c2ecf20Sopenharmony_ci of_node_put(dsp_of_node); 8618c2ecf20Sopenharmony_ci return -EBUSY; 8628c2ecf20Sopenharmony_ci } 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci drvdata = devm_kzalloc(dev, sizeof(struct lpass_data), GFP_KERNEL); 8658c2ecf20Sopenharmony_ci if (!drvdata) 8668c2ecf20Sopenharmony_ci return -ENOMEM; 8678c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, drvdata); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci match = of_match_device(dev->driver->of_match_table, dev); 8708c2ecf20Sopenharmony_ci if (!match || !match->data) 8718c2ecf20Sopenharmony_ci return -EINVAL; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci drvdata->variant = (struct lpass_variant *)match->data; 8748c2ecf20Sopenharmony_ci variant = drvdata->variant; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci of_lpass_cpu_parse_dai_data(dev, drvdata); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci drvdata->lpaif = devm_ioremap_resource(dev, res); 8818c2ecf20Sopenharmony_ci if (IS_ERR((void const __force *)drvdata->lpaif)) { 8828c2ecf20Sopenharmony_ci dev_err(dev, "error mapping reg resource: %ld\n", 8838c2ecf20Sopenharmony_ci PTR_ERR((void const __force *)drvdata->lpaif)); 8848c2ecf20Sopenharmony_ci return PTR_ERR((void const __force *)drvdata->lpaif); 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant, 8888c2ecf20Sopenharmony_ci variant->wrdma_channels + 8898c2ecf20Sopenharmony_ci variant->wrdma_channel_start); 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif, 8928c2ecf20Sopenharmony_ci &lpass_cpu_regmap_config); 8938c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->lpaif_map)) { 8948c2ecf20Sopenharmony_ci dev_err(dev, "error initializing regmap: %ld\n", 8958c2ecf20Sopenharmony_ci PTR_ERR(drvdata->lpaif_map)); 8968c2ecf20Sopenharmony_ci return PTR_ERR(drvdata->lpaif_map); 8978c2ecf20Sopenharmony_ci } 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (drvdata->hdmi_port_enable) { 9008c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-hdmiif"); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci drvdata->hdmiif = devm_ioremap_resource(dev, res); 9038c2ecf20Sopenharmony_ci if (IS_ERR((void const __force *)drvdata->hdmiif)) { 9048c2ecf20Sopenharmony_ci dev_err(dev, "error mapping reg resource: %ld\n", 9058c2ecf20Sopenharmony_ci PTR_ERR((void const __force *)drvdata->hdmiif)); 9068c2ecf20Sopenharmony_ci return PTR_ERR((void const __force *)drvdata->hdmiif); 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant, 9108c2ecf20Sopenharmony_ci variant->hdmi_rdma_channels - 1); 9118c2ecf20Sopenharmony_ci drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif, 9128c2ecf20Sopenharmony_ci &lpass_hdmi_regmap_config); 9138c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->hdmiif_map)) { 9148c2ecf20Sopenharmony_ci dev_err(dev, "error initializing regmap: %ld\n", 9158c2ecf20Sopenharmony_ci PTR_ERR(drvdata->hdmiif_map)); 9168c2ecf20Sopenharmony_ci return PTR_ERR(drvdata->hdmiif_map); 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci } 9198c2ecf20Sopenharmony_ci 9208c2ecf20Sopenharmony_ci if (variant->init) { 9218c2ecf20Sopenharmony_ci ret = variant->init(pdev); 9228c2ecf20Sopenharmony_ci if (ret) { 9238c2ecf20Sopenharmony_ci dev_err(dev, "error initializing variant: %d\n", ret); 9248c2ecf20Sopenharmony_ci return ret; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci for (i = 0; i < variant->num_dai; i++) { 9298c2ecf20Sopenharmony_ci dai_id = variant->dai_driver[i].id; 9308c2ecf20Sopenharmony_ci if (dai_id == LPASS_DP_RX) 9318c2ecf20Sopenharmony_ci continue; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev, 9348c2ecf20Sopenharmony_ci variant->dai_osr_clk_names[i]); 9358c2ecf20Sopenharmony_ci drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(dev, 9368c2ecf20Sopenharmony_ci variant->dai_bit_clk_names[i]); 9378c2ecf20Sopenharmony_ci if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { 9388c2ecf20Sopenharmony_ci dev_err(dev, 9398c2ecf20Sopenharmony_ci "error getting %s: %ld\n", 9408c2ecf20Sopenharmony_ci variant->dai_bit_clk_names[i], 9418c2ecf20Sopenharmony_ci PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); 9428c2ecf20Sopenharmony_ci return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci /* Allocation for i2sctl regmap fields */ 9478c2ecf20Sopenharmony_ci drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl), 9488c2ecf20Sopenharmony_ci GFP_KERNEL); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* Initialize bitfields for dai I2SCTL register */ 9518c2ecf20Sopenharmony_ci ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl, 9528c2ecf20Sopenharmony_ci drvdata->lpaif_map); 9538c2ecf20Sopenharmony_ci if (ret) { 9548c2ecf20Sopenharmony_ci dev_err(dev, "error init i2sctl field: %d\n", ret); 9558c2ecf20Sopenharmony_ci return ret; 9568c2ecf20Sopenharmony_ci } 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci if (drvdata->hdmi_port_enable) { 9598c2ecf20Sopenharmony_ci ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map); 9608c2ecf20Sopenharmony_ci if (ret) { 9618c2ecf20Sopenharmony_ci dev_err(dev, "%s error hdmi init failed\n", __func__); 9628c2ecf20Sopenharmony_ci return ret; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci } 9658c2ecf20Sopenharmony_ci ret = devm_snd_soc_register_component(dev, 9668c2ecf20Sopenharmony_ci &lpass_cpu_comp_driver, 9678c2ecf20Sopenharmony_ci variant->dai_driver, 9688c2ecf20Sopenharmony_ci variant->num_dai); 9698c2ecf20Sopenharmony_ci if (ret) { 9708c2ecf20Sopenharmony_ci dev_err(dev, "error registering cpu driver: %d\n", ret); 9718c2ecf20Sopenharmony_ci goto err; 9728c2ecf20Sopenharmony_ci } 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ret = asoc_qcom_lpass_platform_register(pdev); 9758c2ecf20Sopenharmony_ci if (ret) { 9768c2ecf20Sopenharmony_ci dev_err(dev, "error registering platform driver: %d\n", ret); 9778c2ecf20Sopenharmony_ci goto err; 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_cierr: 9818c2ecf20Sopenharmony_ci return ret; 9828c2ecf20Sopenharmony_ci} 9838c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ciint asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) 9868c2ecf20Sopenharmony_ci{ 9878c2ecf20Sopenharmony_ci struct lpass_data *drvdata = platform_get_drvdata(pdev); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (drvdata->variant->exit) 9908c2ecf20Sopenharmony_ci drvdata->variant->exit(pdev); 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci return 0; 9948c2ecf20Sopenharmony_ci} 9958c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QTi LPASS CPU Driver"); 9988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 999