162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/of_device.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <sound/pcm.h> 1562306a36Sopenharmony_ci#include <sound/pcm_params.h> 1662306a36Sopenharmony_ci#include <linux/regmap.h> 1762306a36Sopenharmony_ci#include <sound/soc.h> 1862306a36Sopenharmony_ci#include <sound/soc-dai.h> 1962306a36Sopenharmony_ci#include "lpass-lpaif-reg.h" 2062306a36Sopenharmony_ci#include "lpass.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define LPASS_CPU_MAX_MI2S_LINES 4 2362306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD0_MASK BIT(0) 2462306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD1_MASK BIT(1) 2562306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD2_MASK BIT(2) 2662306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD3_MASK BIT(3) 2762306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD0_1_MASK GENMASK(1, 0) 2862306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD2_3_MASK GENMASK(3, 2) 2962306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0) 3062306a36Sopenharmony_ci#define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0) 3162306a36Sopenharmony_ci#define LPASS_REG_READ 1 3262306a36Sopenharmony_ci#define LPASS_REG_WRITE 0 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Channel maps for Quad channel playbacks on MI2S Secondary 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic struct snd_pcm_chmap_elem lpass_quad_chmaps[] = { 3862306a36Sopenharmony_ci { .channels = 4, 3962306a36Sopenharmony_ci .map = { SNDRV_CHMAP_FL, SNDRV_CHMAP_RL, 4062306a36Sopenharmony_ci SNDRV_CHMAP_FR, SNDRV_CHMAP_RR } }, 4162306a36Sopenharmony_ci { } 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_cistatic int lpass_cpu_init_i2sctl_bitfields(struct device *dev, 4462306a36Sopenharmony_ci struct lpaif_i2sctl *i2sctl, struct regmap *map) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 4762306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback); 5062306a36Sopenharmony_ci i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken); 5162306a36Sopenharmony_ci i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode); 5262306a36Sopenharmony_ci i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono); 5362306a36Sopenharmony_ci i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen); 5462306a36Sopenharmony_ci i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode); 5562306a36Sopenharmony_ci i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono); 5662306a36Sopenharmony_ci i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc); 5762306a36Sopenharmony_ci i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) || 6062306a36Sopenharmony_ci IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) || 6162306a36Sopenharmony_ci IS_ERR(i2sctl->micen) || IS_ERR(i2sctl->micmode) || 6262306a36Sopenharmony_ci IS_ERR(i2sctl->micmono) || IS_ERR(i2sctl->wssrc) || 6362306a36Sopenharmony_ci IS_ERR(i2sctl->bitwidth)) 6462306a36Sopenharmony_ci return -EINVAL; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, 7062306a36Sopenharmony_ci unsigned int freq, int dir) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 7362306a36Sopenharmony_ci int ret; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq); 7662306a36Sopenharmony_ci if (ret) 7762306a36Sopenharmony_ci dev_err(dai->dev, "error setting mi2s osrclk to %u: %d\n", 7862306a36Sopenharmony_ci freq, ret); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return ret; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, 8462306a36Sopenharmony_ci struct snd_soc_dai *dai) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 8762306a36Sopenharmony_ci int ret; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci ret = clk_prepare_enable(drvdata->mi2s_osr_clk[dai->driver->id]); 9062306a36Sopenharmony_ci if (ret) { 9162306a36Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret); 9262306a36Sopenharmony_ci return ret; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]); 9562306a36Sopenharmony_ci if (ret) { 9662306a36Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 9762306a36Sopenharmony_ci clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); 9862306a36Sopenharmony_ci return ret; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, 10462306a36Sopenharmony_ci struct snd_soc_dai *dai) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 10762306a36Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 10862306a36Sopenharmony_ci unsigned int id = dai->driver->id; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Ensure LRCLK is disabled even in device node validation. 11362306a36Sopenharmony_ci * Will not impact if disabled in lpass_cpu_daiops_trigger() 11462306a36Sopenharmony_ci * suspend. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 11762306a36Sopenharmony_ci regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE); 11862306a36Sopenharmony_ci else 11962306a36Sopenharmony_ci regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* 12262306a36Sopenharmony_ci * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before 12362306a36Sopenharmony_ci * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in 12462306a36Sopenharmony_ci * lpass_cpu_daiops_prepare. 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_ci if (drvdata->mi2s_was_prepared[dai->driver->id]) { 12762306a36Sopenharmony_ci drvdata->mi2s_was_prepared[dai->driver->id] = false; 12862306a36Sopenharmony_ci clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, 13562306a36Sopenharmony_ci struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 13862306a36Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 13962306a36Sopenharmony_ci unsigned int id = dai->driver->id; 14062306a36Sopenharmony_ci snd_pcm_format_t format = params_format(params); 14162306a36Sopenharmony_ci unsigned int channels = params_channels(params); 14262306a36Sopenharmony_ci unsigned int rate = params_rate(params); 14362306a36Sopenharmony_ci unsigned int mode; 14462306a36Sopenharmony_ci unsigned int regval; 14562306a36Sopenharmony_ci int bitwidth, ret; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci bitwidth = snd_pcm_format_width(format); 14862306a36Sopenharmony_ci if (bitwidth < 0) { 14962306a36Sopenharmony_ci dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth); 15062306a36Sopenharmony_ci return bitwidth; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->loopback, id, 15462306a36Sopenharmony_ci LPAIF_I2SCTL_LOOPBACK_DISABLE); 15562306a36Sopenharmony_ci if (ret) { 15662306a36Sopenharmony_ci dev_err(dai->dev, "error updating loopback field: %d\n", ret); 15762306a36Sopenharmony_ci return ret; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->wssrc, id, 16162306a36Sopenharmony_ci LPAIF_I2SCTL_WSSRC_INTERNAL); 16262306a36Sopenharmony_ci if (ret) { 16362306a36Sopenharmony_ci dev_err(dai->dev, "error updating wssrc field: %d\n", ret); 16462306a36Sopenharmony_ci return ret; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci switch (bitwidth) { 16862306a36Sopenharmony_ci case 16: 16962306a36Sopenharmony_ci regval = LPAIF_I2SCTL_BITWIDTH_16; 17062306a36Sopenharmony_ci break; 17162306a36Sopenharmony_ci case 24: 17262306a36Sopenharmony_ci regval = LPAIF_I2SCTL_BITWIDTH_24; 17362306a36Sopenharmony_ci break; 17462306a36Sopenharmony_ci case 32: 17562306a36Sopenharmony_ci regval = LPAIF_I2SCTL_BITWIDTH_32; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci default: 17862306a36Sopenharmony_ci dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth); 17962306a36Sopenharmony_ci return -EINVAL; 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->bitwidth, id, regval); 18362306a36Sopenharmony_ci if (ret) { 18462306a36Sopenharmony_ci dev_err(dai->dev, "error updating bitwidth field: %d\n", ret); 18562306a36Sopenharmony_ci return ret; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 18962306a36Sopenharmony_ci mode = drvdata->mi2s_playback_sd_mode[id]; 19062306a36Sopenharmony_ci else 19162306a36Sopenharmony_ci mode = drvdata->mi2s_capture_sd_mode[id]; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (!mode) { 19462306a36Sopenharmony_ci dev_err(dai->dev, "no line is assigned\n"); 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci switch (channels) { 19962306a36Sopenharmony_ci case 1: 20062306a36Sopenharmony_ci case 2: 20162306a36Sopenharmony_ci switch (mode) { 20262306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_QUAD01: 20362306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_6CH: 20462306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_8CH: 20562306a36Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_SD0; 20662306a36Sopenharmony_ci break; 20762306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_QUAD23: 20862306a36Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_SD2; 20962306a36Sopenharmony_ci break; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci break; 21362306a36Sopenharmony_ci case 4: 21462306a36Sopenharmony_ci if (mode < LPAIF_I2SCTL_MODE_QUAD01) { 21562306a36Sopenharmony_ci dev_err(dai->dev, "cannot configure 4 channels with mode %d\n", 21662306a36Sopenharmony_ci mode); 21762306a36Sopenharmony_ci return -EINVAL; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci switch (mode) { 22162306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_6CH: 22262306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_8CH: 22362306a36Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_QUAD01; 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci case 6: 22862306a36Sopenharmony_ci if (mode < LPAIF_I2SCTL_MODE_6CH) { 22962306a36Sopenharmony_ci dev_err(dai->dev, "cannot configure 6 channels with mode %d\n", 23062306a36Sopenharmony_ci mode); 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci switch (mode) { 23562306a36Sopenharmony_ci case LPAIF_I2SCTL_MODE_8CH: 23662306a36Sopenharmony_ci mode = LPAIF_I2SCTL_MODE_6CH; 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci case 8: 24162306a36Sopenharmony_ci if (mode < LPAIF_I2SCTL_MODE_8CH) { 24262306a36Sopenharmony_ci dev_err(dai->dev, "cannot configure 8 channels with mode %d\n", 24362306a36Sopenharmony_ci mode); 24462306a36Sopenharmony_ci return -EINVAL; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci break; 24762306a36Sopenharmony_ci default: 24862306a36Sopenharmony_ci dev_err(dai->dev, "invalid channels given: %u\n", channels); 24962306a36Sopenharmony_ci return -EINVAL; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 25362306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->spkmode, id, 25462306a36Sopenharmony_ci LPAIF_I2SCTL_SPKMODE(mode)); 25562306a36Sopenharmony_ci if (ret) { 25662306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n", 25762306a36Sopenharmony_ci ret); 25862306a36Sopenharmony_ci return ret; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci if (channels >= 2) 26162306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->spkmono, id, 26262306a36Sopenharmony_ci LPAIF_I2SCTL_SPKMONO_STEREO); 26362306a36Sopenharmony_ci else 26462306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->spkmono, id, 26562306a36Sopenharmony_ci LPAIF_I2SCTL_SPKMONO_MONO); 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->micmode, id, 26862306a36Sopenharmony_ci LPAIF_I2SCTL_MICMODE(mode)); 26962306a36Sopenharmony_ci if (ret) { 27062306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n", 27162306a36Sopenharmony_ci ret); 27262306a36Sopenharmony_ci return ret; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (channels >= 2) 27562306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->micmono, id, 27662306a36Sopenharmony_ci LPAIF_I2SCTL_MICMONO_STEREO); 27762306a36Sopenharmony_ci else 27862306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->micmono, id, 27962306a36Sopenharmony_ci LPAIF_I2SCTL_MICMONO_MONO); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (ret) { 28362306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n", 28462306a36Sopenharmony_ci ret); 28562306a36Sopenharmony_ci return ret; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci ret = clk_set_rate(drvdata->mi2s_bit_clk[id], 28962306a36Sopenharmony_ci rate * bitwidth * 2); 29062306a36Sopenharmony_ci if (ret) { 29162306a36Sopenharmony_ci dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n", 29262306a36Sopenharmony_ci rate * bitwidth * 2, ret); 29362306a36Sopenharmony_ci return ret; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return 0; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, 30062306a36Sopenharmony_ci int cmd, struct snd_soc_dai *dai) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 30362306a36Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 30462306a36Sopenharmony_ci unsigned int id = dai->driver->id; 30562306a36Sopenharmony_ci int ret = -EINVAL; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci switch (cmd) { 30862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 30962306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_RESUME: 31062306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * Ensure lpass BCLK/LRCLK is enabled during 31362306a36Sopenharmony_ci * device resume as lpass_cpu_daiops_prepare() is not called 31462306a36Sopenharmony_ci * after the device resumes. We don't check mi2s_was_prepared before 31562306a36Sopenharmony_ci * enable/disable BCLK in trigger events because: 31662306a36Sopenharmony_ci * 1. These trigger events are paired, so the BCLK 31762306a36Sopenharmony_ci * enable_count is balanced. 31862306a36Sopenharmony_ci * 2. the BCLK can be shared (ex: headset and headset mic), 31962306a36Sopenharmony_ci * we need to increase the enable_count so that we don't 32062306a36Sopenharmony_ci * turn off the shared BCLK while other devices are using 32162306a36Sopenharmony_ci * it. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 32462306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->spken, id, 32562306a36Sopenharmony_ci LPAIF_I2SCTL_SPKEN_ENABLE); 32662306a36Sopenharmony_ci } else { 32762306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->micen, id, 32862306a36Sopenharmony_ci LPAIF_I2SCTL_MICEN_ENABLE); 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci if (ret) 33162306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", 33262306a36Sopenharmony_ci ret); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = clk_enable(drvdata->mi2s_bit_clk[id]); 33562306a36Sopenharmony_ci if (ret) { 33662306a36Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 33762306a36Sopenharmony_ci clk_disable(drvdata->mi2s_osr_clk[id]); 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 34262306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_SUSPEND: 34362306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * To ensure lpass BCLK/LRCLK is disabled during 34662306a36Sopenharmony_ci * device suspend. 34762306a36Sopenharmony_ci */ 34862306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 34962306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->spken, id, 35062306a36Sopenharmony_ci LPAIF_I2SCTL_SPKEN_DISABLE); 35162306a36Sopenharmony_ci } else { 35262306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->micen, id, 35362306a36Sopenharmony_ci LPAIF_I2SCTL_MICEN_DISABLE); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (ret) 35662306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", 35762306a36Sopenharmony_ci ret); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, 36862306a36Sopenharmony_ci struct snd_soc_dai *dai) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 37162306a36Sopenharmony_ci struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; 37262306a36Sopenharmony_ci unsigned int id = dai->driver->id; 37362306a36Sopenharmony_ci int ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture 37762306a36Sopenharmony_ci * data flow starts. This allows other codec to have some delay before 37862306a36Sopenharmony_ci * the data flow. 37962306a36Sopenharmony_ci * (ex: to drop start up pop noise before capture starts). 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 38262306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE); 38362306a36Sopenharmony_ci else 38462306a36Sopenharmony_ci ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (ret) { 38762306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); 38862306a36Sopenharmony_ci return ret; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* 39262306a36Sopenharmony_ci * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can 39362306a36Sopenharmony_ci * be called multiple times. It's paired with the clk_disable in 39462306a36Sopenharmony_ci * lpass_cpu_daiops_shutdown. 39562306a36Sopenharmony_ci */ 39662306a36Sopenharmony_ci if (!drvdata->mi2s_was_prepared[dai->driver->id]) { 39762306a36Sopenharmony_ci ret = clk_enable(drvdata->mi2s_bit_clk[id]); 39862306a36Sopenharmony_ci if (ret) { 39962306a36Sopenharmony_ci dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); 40062306a36Sopenharmony_ci return ret; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci drvdata->mi2s_was_prepared[dai->driver->id] = true; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic int lpass_cpu_daiops_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci int ret; 41062306a36Sopenharmony_ci struct snd_soc_dai_driver *drv = dai->driver; 41162306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (drvdata->mi2s_playback_sd_mode[dai->id] == LPAIF_I2SCTL_MODE_QUAD01) { 41462306a36Sopenharmony_ci ret = snd_pcm_add_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK, 41562306a36Sopenharmony_ci lpass_quad_chmaps, drv->playback.channels_max, 0, 41662306a36Sopenharmony_ci NULL); 41762306a36Sopenharmony_ci if (ret < 0) 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic int lpass_cpu_daiops_probe(struct snd_soc_dai *dai) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 42762306a36Sopenharmony_ci int ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* ensure audio hardware is disabled */ 43062306a36Sopenharmony_ci ret = regmap_write(drvdata->lpaif_map, 43162306a36Sopenharmony_ci LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0); 43262306a36Sopenharmony_ci if (ret) 43362306a36Sopenharmony_ci dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return ret; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciconst struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { 43962306a36Sopenharmony_ci .probe = lpass_cpu_daiops_probe, 44062306a36Sopenharmony_ci .set_sysclk = lpass_cpu_daiops_set_sysclk, 44162306a36Sopenharmony_ci .startup = lpass_cpu_daiops_startup, 44262306a36Sopenharmony_ci .shutdown = lpass_cpu_daiops_shutdown, 44362306a36Sopenharmony_ci .hw_params = lpass_cpu_daiops_hw_params, 44462306a36Sopenharmony_ci .trigger = lpass_cpu_daiops_trigger, 44562306a36Sopenharmony_ci .prepare = lpass_cpu_daiops_prepare, 44662306a36Sopenharmony_ci}; 44762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ciconst struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops2 = { 45062306a36Sopenharmony_ci .pcm_new = lpass_cpu_daiops_pcm_new, 45162306a36Sopenharmony_ci .probe = lpass_cpu_daiops_probe, 45262306a36Sopenharmony_ci .set_sysclk = lpass_cpu_daiops_set_sysclk, 45362306a36Sopenharmony_ci .startup = lpass_cpu_daiops_startup, 45462306a36Sopenharmony_ci .shutdown = lpass_cpu_daiops_shutdown, 45562306a36Sopenharmony_ci .hw_params = lpass_cpu_daiops_hw_params, 45662306a36Sopenharmony_ci .trigger = lpass_cpu_daiops_trigger, 45762306a36Sopenharmony_ci .prepare = lpass_cpu_daiops_prepare, 45862306a36Sopenharmony_ci}; 45962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops2); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, 46262306a36Sopenharmony_ci const struct of_phandle_args *args, 46362306a36Sopenharmony_ci const char **dai_name) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); 46662306a36Sopenharmony_ci struct lpass_variant *variant = drvdata->variant; 46762306a36Sopenharmony_ci int id = args->args[0]; 46862306a36Sopenharmony_ci int ret = -EINVAL; 46962306a36Sopenharmony_ci int i; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci for (i = 0; i < variant->num_dai; i++) { 47262306a36Sopenharmony_ci if (variant->dai_driver[i].id == id) { 47362306a36Sopenharmony_ci *dai_name = variant->dai_driver[i].name; 47462306a36Sopenharmony_ci ret = 0; 47562306a36Sopenharmony_ci break; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return ret; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic const struct snd_soc_component_driver lpass_cpu_comp_driver = { 48362306a36Sopenharmony_ci .name = "lpass-cpu", 48462306a36Sopenharmony_ci .of_xlate_dai_name = asoc_qcom_of_xlate_dai_name, 48562306a36Sopenharmony_ci .legacy_dai_naming = 1, 48662306a36Sopenharmony_ci}; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 49162306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 49262306a36Sopenharmony_ci int i; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci for (i = 0; i < v->i2s_ports; ++i) 49562306a36Sopenharmony_ci if (reg == LPAIF_I2SCTL_REG(v, i)) 49662306a36Sopenharmony_ci return true; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci for (i = 0; i < v->irq_ports; ++i) { 49962306a36Sopenharmony_ci if (reg == LPAIF_IRQEN_REG(v, i)) 50062306a36Sopenharmony_ci return true; 50162306a36Sopenharmony_ci if (reg == LPAIF_IRQCLEAR_REG(v, i)) 50262306a36Sopenharmony_ci return true; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci for (i = 0; i < v->rdma_channels; ++i) { 50662306a36Sopenharmony_ci if (reg == LPAIF_RDMACTL_REG(v, i)) 50762306a36Sopenharmony_ci return true; 50862306a36Sopenharmony_ci if (reg == LPAIF_RDMABASE_REG(v, i)) 50962306a36Sopenharmony_ci return true; 51062306a36Sopenharmony_ci if (reg == LPAIF_RDMABUFF_REG(v, i)) 51162306a36Sopenharmony_ci return true; 51262306a36Sopenharmony_ci if (reg == LPAIF_RDMAPER_REG(v, i)) 51362306a36Sopenharmony_ci return true; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci for (i = 0; i < v->wrdma_channels; ++i) { 51762306a36Sopenharmony_ci if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) 51862306a36Sopenharmony_ci return true; 51962306a36Sopenharmony_ci if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) 52062306a36Sopenharmony_ci return true; 52162306a36Sopenharmony_ci if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) 52262306a36Sopenharmony_ci return true; 52362306a36Sopenharmony_ci if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) 52462306a36Sopenharmony_ci return true; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return false; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistatic bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 53362306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 53462306a36Sopenharmony_ci int i; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci for (i = 0; i < v->i2s_ports; ++i) 53762306a36Sopenharmony_ci if (reg == LPAIF_I2SCTL_REG(v, i)) 53862306a36Sopenharmony_ci return true; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for (i = 0; i < v->irq_ports; ++i) { 54162306a36Sopenharmony_ci if (reg == LPAIF_IRQCLEAR_REG(v, i)) 54262306a36Sopenharmony_ci return true; 54362306a36Sopenharmony_ci if (reg == LPAIF_IRQEN_REG(v, i)) 54462306a36Sopenharmony_ci return true; 54562306a36Sopenharmony_ci if (reg == LPAIF_IRQSTAT_REG(v, i)) 54662306a36Sopenharmony_ci return true; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci for (i = 0; i < v->rdma_channels; ++i) { 55062306a36Sopenharmony_ci if (reg == LPAIF_RDMACTL_REG(v, i)) 55162306a36Sopenharmony_ci return true; 55262306a36Sopenharmony_ci if (reg == LPAIF_RDMABASE_REG(v, i)) 55362306a36Sopenharmony_ci return true; 55462306a36Sopenharmony_ci if (reg == LPAIF_RDMABUFF_REG(v, i)) 55562306a36Sopenharmony_ci return true; 55662306a36Sopenharmony_ci if (reg == LPAIF_RDMACURR_REG(v, i)) 55762306a36Sopenharmony_ci return true; 55862306a36Sopenharmony_ci if (reg == LPAIF_RDMAPER_REG(v, i)) 55962306a36Sopenharmony_ci return true; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci for (i = 0; i < v->wrdma_channels; ++i) { 56362306a36Sopenharmony_ci if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start)) 56462306a36Sopenharmony_ci return true; 56562306a36Sopenharmony_ci if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start)) 56662306a36Sopenharmony_ci return true; 56762306a36Sopenharmony_ci if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start)) 56862306a36Sopenharmony_ci return true; 56962306a36Sopenharmony_ci if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) 57062306a36Sopenharmony_ci return true; 57162306a36Sopenharmony_ci if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start)) 57262306a36Sopenharmony_ci return true; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return false; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 58162306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 58262306a36Sopenharmony_ci int i; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (i = 0; i < v->irq_ports; ++i) { 58562306a36Sopenharmony_ci if (reg == LPAIF_IRQCLEAR_REG(v, i)) 58662306a36Sopenharmony_ci return true; 58762306a36Sopenharmony_ci if (reg == LPAIF_IRQSTAT_REG(v, i)) 58862306a36Sopenharmony_ci return true; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci for (i = 0; i < v->rdma_channels; ++i) 59262306a36Sopenharmony_ci if (reg == LPAIF_RDMACURR_REG(v, i)) 59362306a36Sopenharmony_ci return true; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci for (i = 0; i < v->wrdma_channels; ++i) 59662306a36Sopenharmony_ci if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start)) 59762306a36Sopenharmony_ci return true; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci return false; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic struct regmap_config lpass_cpu_regmap_config = { 60362306a36Sopenharmony_ci .name = "lpass_cpu", 60462306a36Sopenharmony_ci .reg_bits = 32, 60562306a36Sopenharmony_ci .reg_stride = 4, 60662306a36Sopenharmony_ci .val_bits = 32, 60762306a36Sopenharmony_ci .writeable_reg = lpass_cpu_regmap_writeable, 60862306a36Sopenharmony_ci .readable_reg = lpass_cpu_regmap_readable, 60962306a36Sopenharmony_ci .volatile_reg = lpass_cpu_regmap_volatile, 61062306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 61662306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 61762306a36Sopenharmony_ci unsigned int i; 61862306a36Sopenharmony_ci struct lpass_hdmi_tx_ctl *tx_ctl; 61962306a36Sopenharmony_ci struct regmap_field *legacy_en; 62062306a36Sopenharmony_ci struct lpass_vbit_ctrl *vbit_ctl; 62162306a36Sopenharmony_ci struct regmap_field *tx_parity; 62262306a36Sopenharmony_ci struct lpass_dp_metadata_ctl *meta_ctl; 62362306a36Sopenharmony_ci struct lpass_sstream_ctl *sstream_ctl; 62462306a36Sopenharmony_ci struct regmap_field *ch_msb; 62562306a36Sopenharmony_ci struct regmap_field *ch_lsb; 62662306a36Sopenharmony_ci struct lpass_hdmitx_dmactl *tx_dmactl; 62762306a36Sopenharmony_ci int rval; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL); 63062306a36Sopenharmony_ci if (!tx_ctl) 63162306a36Sopenharmony_ci return -ENOMEM; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset); 63462306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset); 63562306a36Sopenharmony_ci drvdata->tx_ctl = tx_ctl; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en); 63862306a36Sopenharmony_ci drvdata->hdmitx_legacy_en = legacy_en; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL); 64162306a36Sopenharmony_ci if (!vbit_ctl) 64262306a36Sopenharmony_ci return -ENOMEM; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit); 64562306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream); 64662306a36Sopenharmony_ci drvdata->vbit_ctl = vbit_ctl; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity); 65062306a36Sopenharmony_ci drvdata->hdmitx_parity_calc_en = tx_parity; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL); 65362306a36Sopenharmony_ci if (!meta_ctl) 65462306a36Sopenharmony_ci return -ENOMEM; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7); 65762306a36Sopenharmony_ci if (rval) 65862306a36Sopenharmony_ci return rval; 65962306a36Sopenharmony_ci drvdata->meta_ctl = meta_ctl; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL); 66262306a36Sopenharmony_ci if (!sstream_ctl) 66362306a36Sopenharmony_ci return -ENOMEM; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9); 66662306a36Sopenharmony_ci if (rval) 66762306a36Sopenharmony_ci return rval; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci drvdata->sstream_ctl = sstream_ctl; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) { 67262306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb); 67362306a36Sopenharmony_ci drvdata->hdmitx_ch_msb[i] = ch_msb; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb); 67662306a36Sopenharmony_ci drvdata->hdmitx_ch_lsb[i] = ch_lsb; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL); 67962306a36Sopenharmony_ci if (!tx_dmactl) 68062306a36Sopenharmony_ci return -ENOMEM; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs); 68362306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr); 68462306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel); 68562306a36Sopenharmony_ci QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel); 68662306a36Sopenharmony_ci drvdata->hdmi_tx_dmactl[i] = tx_dmactl; 68762306a36Sopenharmony_ci } 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_cistatic bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 69462306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 69562306a36Sopenharmony_ci int i; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) 69862306a36Sopenharmony_ci return true; 69962306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) 70062306a36Sopenharmony_ci return true; 70162306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 70262306a36Sopenharmony_ci return true; 70362306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 70462306a36Sopenharmony_ci return true; 70562306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_DP_ADDR(v)) 70662306a36Sopenharmony_ci return true; 70762306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) 70862306a36Sopenharmony_ci return true; 70962306a36Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) 71062306a36Sopenharmony_ci return true; 71162306a36Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v)) 71262306a36Sopenharmony_ci return true; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; i++) { 71562306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) 71662306a36Sopenharmony_ci return true; 71762306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) 71862306a36Sopenharmony_ci return true; 71962306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) 72062306a36Sopenharmony_ci return true; 72162306a36Sopenharmony_ci } 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; ++i) { 72462306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) 72562306a36Sopenharmony_ci return true; 72662306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) 72762306a36Sopenharmony_ci return true; 72862306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) 72962306a36Sopenharmony_ci return true; 73062306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) 73162306a36Sopenharmony_ci return true; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci return false; 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 73962306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 74062306a36Sopenharmony_ci int i; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) 74362306a36Sopenharmony_ci return true; 74462306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) 74562306a36Sopenharmony_ci return true; 74662306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 74762306a36Sopenharmony_ci return true; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; i++) { 75062306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) 75162306a36Sopenharmony_ci return true; 75262306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) 75362306a36Sopenharmony_ci return true; 75462306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) 75562306a36Sopenharmony_ci return true; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 75962306a36Sopenharmony_ci return true; 76062306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_DP_ADDR(v)) 76162306a36Sopenharmony_ci return true; 76262306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) 76362306a36Sopenharmony_ci return true; 76462306a36Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) 76562306a36Sopenharmony_ci return true; 76662306a36Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) 76762306a36Sopenharmony_ci return true; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; ++i) { 77062306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) 77162306a36Sopenharmony_ci return true; 77262306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) 77362306a36Sopenharmony_ci return true; 77462306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) 77562306a36Sopenharmony_ci return true; 77662306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) 77762306a36Sopenharmony_ci return true; 77862306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) 77962306a36Sopenharmony_ci return true; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci return false; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 78862306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 78962306a36Sopenharmony_ci int i; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) 79262306a36Sopenharmony_ci return true; 79362306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) 79462306a36Sopenharmony_ci return true; 79562306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 79662306a36Sopenharmony_ci return true; 79762306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 79862306a36Sopenharmony_ci return true; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci for (i = 0; i < v->hdmi_rdma_channels; ++i) { 80162306a36Sopenharmony_ci if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) 80262306a36Sopenharmony_ci return true; 80362306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) 80462306a36Sopenharmony_ci return true; 80562306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) 80662306a36Sopenharmony_ci return true; 80762306a36Sopenharmony_ci if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) 80862306a36Sopenharmony_ci return true; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci return false; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic struct regmap_config lpass_hdmi_regmap_config = { 81462306a36Sopenharmony_ci .name = "lpass_hdmi", 81562306a36Sopenharmony_ci .reg_bits = 32, 81662306a36Sopenharmony_ci .reg_stride = 4, 81762306a36Sopenharmony_ci .val_bits = 32, 81862306a36Sopenharmony_ci .writeable_reg = lpass_hdmi_regmap_writeable, 81962306a36Sopenharmony_ci .readable_reg = lpass_hdmi_regmap_readable, 82062306a36Sopenharmony_ci .volatile_reg = lpass_hdmi_regmap_volatile, 82162306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 82262306a36Sopenharmony_ci}; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic bool __lpass_rxtx_regmap_accessible(struct device *dev, unsigned int reg, bool rw) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 82762306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 82862306a36Sopenharmony_ci int i; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci for (i = 0; i < v->rxtx_irq_ports; ++i) { 83162306a36Sopenharmony_ci if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i)) 83262306a36Sopenharmony_ci return true; 83362306a36Sopenharmony_ci if (reg == LPAIF_RXTX_IRQEN_REG(v, i)) 83462306a36Sopenharmony_ci return true; 83562306a36Sopenharmony_ci if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i)) 83662306a36Sopenharmony_ci return true; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci for (i = 0; i < v->rxtx_rdma_channels; ++i) { 84062306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMACTL_REG(v, i, LPASS_CDC_DMA_RX0)) 84162306a36Sopenharmony_ci return true; 84262306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMABASE_REG(v, i, LPASS_CDC_DMA_RX0)) 84362306a36Sopenharmony_ci return true; 84462306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMABUFF_REG(v, i, LPASS_CDC_DMA_RX0)) 84562306a36Sopenharmony_ci return true; 84662306a36Sopenharmony_ci if (rw == LPASS_REG_READ) { 84762306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) 84862306a36Sopenharmony_ci return true; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMAPER_REG(v, i, LPASS_CDC_DMA_RX0)) 85162306a36Sopenharmony_ci return true; 85262306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMA_INTF_REG(v, i, LPASS_CDC_DMA_RX0)) 85362306a36Sopenharmony_ci return true; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci for (i = 0; i < v->rxtx_wrdma_channels; ++i) { 85762306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start, 85862306a36Sopenharmony_ci LPASS_CDC_DMA_TX3)) 85962306a36Sopenharmony_ci return true; 86062306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMABASE_REG(v, i + v->rxtx_wrdma_channel_start, 86162306a36Sopenharmony_ci LPASS_CDC_DMA_TX3)) 86262306a36Sopenharmony_ci return true; 86362306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMABUFF_REG(v, i + v->rxtx_wrdma_channel_start, 86462306a36Sopenharmony_ci LPASS_CDC_DMA_TX3)) 86562306a36Sopenharmony_ci return true; 86662306a36Sopenharmony_ci if (rw == LPASS_REG_READ) { 86762306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) 86862306a36Sopenharmony_ci return true; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMAPER_REG(v, i + v->rxtx_wrdma_channel_start, 87162306a36Sopenharmony_ci LPASS_CDC_DMA_TX3)) 87262306a36Sopenharmony_ci return true; 87362306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, i + v->rxtx_wrdma_channel_start, 87462306a36Sopenharmony_ci LPASS_CDC_DMA_TX3)) 87562306a36Sopenharmony_ci return true; 87662306a36Sopenharmony_ci } 87762306a36Sopenharmony_ci return false; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic bool lpass_rxtx_regmap_writeable(struct device *dev, unsigned int reg) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_WRITE); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_cistatic bool lpass_rxtx_regmap_readable(struct device *dev, unsigned int reg) 88662306a36Sopenharmony_ci{ 88762306a36Sopenharmony_ci return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_READ); 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic bool lpass_rxtx_regmap_volatile(struct device *dev, unsigned int reg) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 89362306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 89462306a36Sopenharmony_ci int i; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci for (i = 0; i < v->rxtx_irq_ports; ++i) { 89762306a36Sopenharmony_ci if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i)) 89862306a36Sopenharmony_ci return true; 89962306a36Sopenharmony_ci if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i)) 90062306a36Sopenharmony_ci return true; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci for (i = 0; i < v->rxtx_rdma_channels; ++i) 90462306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0)) 90562306a36Sopenharmony_ci return true; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci for (i = 0; i < v->rxtx_wrdma_channels; ++i) 90862306a36Sopenharmony_ci if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i + v->rxtx_wrdma_channel_start, 90962306a36Sopenharmony_ci LPASS_CDC_DMA_TX3)) 91062306a36Sopenharmony_ci return true; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci return false; 91362306a36Sopenharmony_ci} 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cistatic bool __lpass_va_regmap_accessible(struct device *dev, unsigned int reg, bool rw) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 91862306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 91962306a36Sopenharmony_ci int i; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci for (i = 0; i < v->va_irq_ports; ++i) { 92262306a36Sopenharmony_ci if (reg == LPAIF_VA_IRQCLEAR_REG(v, i)) 92362306a36Sopenharmony_ci return true; 92462306a36Sopenharmony_ci if (reg == LPAIF_VA_IRQEN_REG(v, i)) 92562306a36Sopenharmony_ci return true; 92662306a36Sopenharmony_ci if (reg == LPAIF_VA_IRQSTAT_REG(v, i)) 92762306a36Sopenharmony_ci return true; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci for (i = 0; i < v->va_wrdma_channels; ++i) { 93162306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMACTL_REG(v, i + v->va_wrdma_channel_start, 93262306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 93362306a36Sopenharmony_ci return true; 93462306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMABASE_REG(v, i + v->va_wrdma_channel_start, 93562306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 93662306a36Sopenharmony_ci return true; 93762306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMABUFF_REG(v, i + v->va_wrdma_channel_start, 93862306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 93962306a36Sopenharmony_ci return true; 94062306a36Sopenharmony_ci if (rw == LPASS_REG_READ) { 94162306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start, 94262306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 94362306a36Sopenharmony_ci return true; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMAPER_REG(v, i + v->va_wrdma_channel_start, 94662306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 94762306a36Sopenharmony_ci return true; 94862306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMA_INTF_REG(v, i + v->va_wrdma_channel_start, 94962306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 95062306a36Sopenharmony_ci return true; 95162306a36Sopenharmony_ci } 95262306a36Sopenharmony_ci return false; 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_cistatic bool lpass_va_regmap_writeable(struct device *dev, unsigned int reg) 95662306a36Sopenharmony_ci{ 95762306a36Sopenharmony_ci return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_WRITE); 95862306a36Sopenharmony_ci} 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_cistatic bool lpass_va_regmap_readable(struct device *dev, unsigned int reg) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_READ); 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic bool lpass_va_regmap_volatile(struct device *dev, unsigned int reg) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct lpass_data *drvdata = dev_get_drvdata(dev); 96862306a36Sopenharmony_ci struct lpass_variant *v = drvdata->variant; 96962306a36Sopenharmony_ci int i; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci for (i = 0; i < v->va_irq_ports; ++i) { 97262306a36Sopenharmony_ci if (reg == LPAIF_VA_IRQCLEAR_REG(v, i)) 97362306a36Sopenharmony_ci return true; 97462306a36Sopenharmony_ci if (reg == LPAIF_VA_IRQSTAT_REG(v, i)) 97562306a36Sopenharmony_ci return true; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci for (i = 0; i < v->va_wrdma_channels; ++i) { 97962306a36Sopenharmony_ci if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start, 98062306a36Sopenharmony_ci LPASS_CDC_DMA_VA_TX0)) 98162306a36Sopenharmony_ci return true; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return false; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic struct regmap_config lpass_rxtx_regmap_config = { 98862306a36Sopenharmony_ci .reg_bits = 32, 98962306a36Sopenharmony_ci .reg_stride = 4, 99062306a36Sopenharmony_ci .val_bits = 32, 99162306a36Sopenharmony_ci .writeable_reg = lpass_rxtx_regmap_writeable, 99262306a36Sopenharmony_ci .readable_reg = lpass_rxtx_regmap_readable, 99362306a36Sopenharmony_ci .volatile_reg = lpass_rxtx_regmap_volatile, 99462306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic struct regmap_config lpass_va_regmap_config = { 99862306a36Sopenharmony_ci .reg_bits = 32, 99962306a36Sopenharmony_ci .reg_stride = 4, 100062306a36Sopenharmony_ci .val_bits = 32, 100162306a36Sopenharmony_ci .writeable_reg = lpass_va_regmap_writeable, 100262306a36Sopenharmony_ci .readable_reg = lpass_va_regmap_readable, 100362306a36Sopenharmony_ci .volatile_reg = lpass_va_regmap_volatile, 100462306a36Sopenharmony_ci .cache_type = REGCACHE_FLAT, 100562306a36Sopenharmony_ci}; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, 100862306a36Sopenharmony_ci struct device_node *node, 100962306a36Sopenharmony_ci const char *name) 101062306a36Sopenharmony_ci{ 101162306a36Sopenharmony_ci unsigned int lines[LPASS_CPU_MAX_MI2S_LINES]; 101262306a36Sopenharmony_ci unsigned int sd_line_mask = 0; 101362306a36Sopenharmony_ci int num_lines, i; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci num_lines = of_property_read_variable_u32_array(node, name, lines, 0, 101662306a36Sopenharmony_ci LPASS_CPU_MAX_MI2S_LINES); 101762306a36Sopenharmony_ci if (num_lines < 0) 101862306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_NONE; 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci for (i = 0; i < num_lines; i++) 102162306a36Sopenharmony_ci sd_line_mask |= BIT(lines[i]); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci switch (sd_line_mask) { 102462306a36Sopenharmony_ci case LPASS_CPU_I2S_SD0_MASK: 102562306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD0; 102662306a36Sopenharmony_ci case LPASS_CPU_I2S_SD1_MASK: 102762306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD1; 102862306a36Sopenharmony_ci case LPASS_CPU_I2S_SD2_MASK: 102962306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD2; 103062306a36Sopenharmony_ci case LPASS_CPU_I2S_SD3_MASK: 103162306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_SD3; 103262306a36Sopenharmony_ci case LPASS_CPU_I2S_SD0_1_MASK: 103362306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_QUAD01; 103462306a36Sopenharmony_ci case LPASS_CPU_I2S_SD2_3_MASK: 103562306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_QUAD23; 103662306a36Sopenharmony_ci case LPASS_CPU_I2S_SD0_1_2_MASK: 103762306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_6CH; 103862306a36Sopenharmony_ci case LPASS_CPU_I2S_SD0_1_2_3_MASK: 103962306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_8CH; 104062306a36Sopenharmony_ci default: 104162306a36Sopenharmony_ci dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask); 104262306a36Sopenharmony_ci return LPAIF_I2SCTL_MODE_NONE; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic void of_lpass_cpu_parse_dai_data(struct device *dev, 104762306a36Sopenharmony_ci struct lpass_data *data) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct device_node *node; 105062306a36Sopenharmony_ci int ret, i, id; 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* Allow all channels by default for backwards compatibility */ 105362306a36Sopenharmony_ci for (i = 0; i < data->variant->num_dai; i++) { 105462306a36Sopenharmony_ci id = data->variant->dai_driver[i].id; 105562306a36Sopenharmony_ci data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; 105662306a36Sopenharmony_ci data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci for_each_child_of_node(dev->of_node, node) { 106062306a36Sopenharmony_ci ret = of_property_read_u32(node, "reg", &id); 106162306a36Sopenharmony_ci if (ret || id < 0) { 106262306a36Sopenharmony_ci dev_err(dev, "valid dai id not found: %d\n", ret); 106362306a36Sopenharmony_ci continue; 106462306a36Sopenharmony_ci } 106562306a36Sopenharmony_ci if (id == LPASS_DP_RX) { 106662306a36Sopenharmony_ci data->hdmi_port_enable = 1; 106762306a36Sopenharmony_ci } else if (is_cdc_dma_port(id)) { 106862306a36Sopenharmony_ci data->codec_dma_enable = 1; 106962306a36Sopenharmony_ci } else { 107062306a36Sopenharmony_ci data->mi2s_playback_sd_mode[id] = 107162306a36Sopenharmony_ci of_lpass_cpu_parse_sd_lines(dev, node, 107262306a36Sopenharmony_ci "qcom,playback-sd-lines"); 107362306a36Sopenharmony_ci data->mi2s_capture_sd_mode[id] = 107462306a36Sopenharmony_ci of_lpass_cpu_parse_sd_lines(dev, node, 107562306a36Sopenharmony_ci "qcom,capture-sd-lines"); 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int of_lpass_cdc_dma_clks_parse(struct device *dev, 108162306a36Sopenharmony_ci struct lpass_data *data) 108262306a36Sopenharmony_ci{ 108362306a36Sopenharmony_ci data->codec_mem0 = devm_clk_get(dev, "audio_cc_codec_mem0"); 108462306a36Sopenharmony_ci if (IS_ERR(data->codec_mem0)) 108562306a36Sopenharmony_ci return PTR_ERR(data->codec_mem0); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci data->codec_mem1 = devm_clk_get(dev, "audio_cc_codec_mem1"); 108862306a36Sopenharmony_ci if (IS_ERR(data->codec_mem1)) 108962306a36Sopenharmony_ci return PTR_ERR(data->codec_mem1); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci data->codec_mem2 = devm_clk_get(dev, "audio_cc_codec_mem2"); 109262306a36Sopenharmony_ci if (IS_ERR(data->codec_mem2)) 109362306a36Sopenharmony_ci return PTR_ERR(data->codec_mem2); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci data->va_mem0 = devm_clk_get(dev, "aon_cc_va_mem0"); 109662306a36Sopenharmony_ci if (IS_ERR(data->va_mem0)) 109762306a36Sopenharmony_ci return PTR_ERR(data->va_mem0); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci} 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ciint asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) 110362306a36Sopenharmony_ci{ 110462306a36Sopenharmony_ci struct lpass_data *drvdata; 110562306a36Sopenharmony_ci struct device_node *dsp_of_node; 110662306a36Sopenharmony_ci struct resource *res; 110762306a36Sopenharmony_ci struct lpass_variant *variant; 110862306a36Sopenharmony_ci struct device *dev = &pdev->dev; 110962306a36Sopenharmony_ci const struct of_device_id *match; 111062306a36Sopenharmony_ci int ret, i, dai_id; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); 111362306a36Sopenharmony_ci if (dsp_of_node) { 111462306a36Sopenharmony_ci dev_err(dev, "DSP exists and holds audio resources\n"); 111562306a36Sopenharmony_ci of_node_put(dsp_of_node); 111662306a36Sopenharmony_ci return -EBUSY; 111762306a36Sopenharmony_ci } 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci drvdata = devm_kzalloc(dev, sizeof(struct lpass_data), GFP_KERNEL); 112062306a36Sopenharmony_ci if (!drvdata) 112162306a36Sopenharmony_ci return -ENOMEM; 112262306a36Sopenharmony_ci platform_set_drvdata(pdev, drvdata); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci match = of_match_device(dev->driver->of_match_table, dev); 112562306a36Sopenharmony_ci if (!match || !match->data) 112662306a36Sopenharmony_ci return -EINVAL; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (of_device_is_compatible(dev->of_node, "qcom,lpass-cpu-apq8016")) { 112962306a36Sopenharmony_ci dev_warn(dev, "%s compatible is deprecated\n", 113062306a36Sopenharmony_ci match->compatible); 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci drvdata->variant = (struct lpass_variant *)match->data; 113462306a36Sopenharmony_ci variant = drvdata->variant; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci of_lpass_cpu_parse_dai_data(dev, drvdata); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci if (drvdata->codec_dma_enable) { 113962306a36Sopenharmony_ci drvdata->rxtx_lpaif = 114062306a36Sopenharmony_ci devm_platform_ioremap_resource_byname(pdev, "lpass-rxtx-lpaif"); 114162306a36Sopenharmony_ci if (IS_ERR(drvdata->rxtx_lpaif)) 114262306a36Sopenharmony_ci return PTR_ERR(drvdata->rxtx_lpaif); 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci drvdata->va_lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-va-lpaif"); 114562306a36Sopenharmony_ci if (IS_ERR(drvdata->va_lpaif)) 114662306a36Sopenharmony_ci return PTR_ERR(drvdata->va_lpaif); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci lpass_rxtx_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant, 114962306a36Sopenharmony_ci variant->rxtx_wrdma_channels + 115062306a36Sopenharmony_ci variant->rxtx_wrdma_channel_start, LPASS_CDC_DMA_TX3); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci drvdata->rxtx_lpaif_map = devm_regmap_init_mmio(dev, drvdata->rxtx_lpaif, 115362306a36Sopenharmony_ci &lpass_rxtx_regmap_config); 115462306a36Sopenharmony_ci if (IS_ERR(drvdata->rxtx_lpaif_map)) 115562306a36Sopenharmony_ci return PTR_ERR(drvdata->rxtx_lpaif_map); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci lpass_va_regmap_config.max_register = LPAIF_CDC_VA_WRDMAPER_REG(variant, 115862306a36Sopenharmony_ci variant->va_wrdma_channels + 115962306a36Sopenharmony_ci variant->va_wrdma_channel_start, LPASS_CDC_DMA_VA_TX0); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci drvdata->va_lpaif_map = devm_regmap_init_mmio(dev, drvdata->va_lpaif, 116262306a36Sopenharmony_ci &lpass_va_regmap_config); 116362306a36Sopenharmony_ci if (IS_ERR(drvdata->va_lpaif_map)) 116462306a36Sopenharmony_ci return PTR_ERR(drvdata->va_lpaif_map); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci ret = of_lpass_cdc_dma_clks_parse(dev, drvdata); 116762306a36Sopenharmony_ci if (ret) { 116862306a36Sopenharmony_ci dev_err(dev, "failed to get cdc dma clocks %d\n", ret); 116962306a36Sopenharmony_ci return ret; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm"); 117362306a36Sopenharmony_ci drvdata->rxtx_cdc_dma_lpm_buf = res->start; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm"); 117662306a36Sopenharmony_ci drvdata->va_cdc_dma_lpm_buf = res->start; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci drvdata->lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-lpaif"); 118062306a36Sopenharmony_ci if (IS_ERR(drvdata->lpaif)) 118162306a36Sopenharmony_ci return PTR_ERR(drvdata->lpaif); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant, 118462306a36Sopenharmony_ci variant->wrdma_channels + 118562306a36Sopenharmony_ci variant->wrdma_channel_start); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif, 118862306a36Sopenharmony_ci &lpass_cpu_regmap_config); 118962306a36Sopenharmony_ci if (IS_ERR(drvdata->lpaif_map)) { 119062306a36Sopenharmony_ci dev_err(dev, "error initializing regmap: %ld\n", 119162306a36Sopenharmony_ci PTR_ERR(drvdata->lpaif_map)); 119262306a36Sopenharmony_ci return PTR_ERR(drvdata->lpaif_map); 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci if (drvdata->hdmi_port_enable) { 119662306a36Sopenharmony_ci drvdata->hdmiif = devm_platform_ioremap_resource_byname(pdev, "lpass-hdmiif"); 119762306a36Sopenharmony_ci if (IS_ERR(drvdata->hdmiif)) 119862306a36Sopenharmony_ci return PTR_ERR(drvdata->hdmiif); 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant, 120162306a36Sopenharmony_ci variant->hdmi_rdma_channels - 1); 120262306a36Sopenharmony_ci drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif, 120362306a36Sopenharmony_ci &lpass_hdmi_regmap_config); 120462306a36Sopenharmony_ci if (IS_ERR(drvdata->hdmiif_map)) { 120562306a36Sopenharmony_ci dev_err(dev, "error initializing regmap: %ld\n", 120662306a36Sopenharmony_ci PTR_ERR(drvdata->hdmiif_map)); 120762306a36Sopenharmony_ci return PTR_ERR(drvdata->hdmiif_map); 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci } 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if (variant->init) { 121262306a36Sopenharmony_ci ret = variant->init(pdev); 121362306a36Sopenharmony_ci if (ret) { 121462306a36Sopenharmony_ci dev_err(dev, "error initializing variant: %d\n", ret); 121562306a36Sopenharmony_ci return ret; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci for (i = 0; i < variant->num_dai; i++) { 122062306a36Sopenharmony_ci dai_id = variant->dai_driver[i].id; 122162306a36Sopenharmony_ci if (dai_id == LPASS_DP_RX || is_cdc_dma_port(dai_id)) 122262306a36Sopenharmony_ci continue; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev, 122562306a36Sopenharmony_ci variant->dai_osr_clk_names[i]); 122662306a36Sopenharmony_ci drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(dev, 122762306a36Sopenharmony_ci variant->dai_bit_clk_names[i]); 122862306a36Sopenharmony_ci if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { 122962306a36Sopenharmony_ci dev_err(dev, 123062306a36Sopenharmony_ci "error getting %s: %ld\n", 123162306a36Sopenharmony_ci variant->dai_bit_clk_names[i], 123262306a36Sopenharmony_ci PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); 123362306a36Sopenharmony_ci return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci if (drvdata->mi2s_playback_sd_mode[dai_id] == 123662306a36Sopenharmony_ci LPAIF_I2SCTL_MODE_QUAD01) { 123762306a36Sopenharmony_ci variant->dai_driver[dai_id].playback.channels_min = 4; 123862306a36Sopenharmony_ci variant->dai_driver[dai_id].playback.channels_max = 4; 123962306a36Sopenharmony_ci } 124062306a36Sopenharmony_ci } 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci /* Allocation for i2sctl regmap fields */ 124362306a36Sopenharmony_ci drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl), 124462306a36Sopenharmony_ci GFP_KERNEL); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci /* Initialize bitfields for dai I2SCTL register */ 124762306a36Sopenharmony_ci ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl, 124862306a36Sopenharmony_ci drvdata->lpaif_map); 124962306a36Sopenharmony_ci if (ret) { 125062306a36Sopenharmony_ci dev_err(dev, "error init i2sctl field: %d\n", ret); 125162306a36Sopenharmony_ci return ret; 125262306a36Sopenharmony_ci } 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (drvdata->hdmi_port_enable) { 125562306a36Sopenharmony_ci ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map); 125662306a36Sopenharmony_ci if (ret) { 125762306a36Sopenharmony_ci dev_err(dev, "%s error hdmi init failed\n", __func__); 125862306a36Sopenharmony_ci return ret; 125962306a36Sopenharmony_ci } 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci ret = devm_snd_soc_register_component(dev, 126262306a36Sopenharmony_ci &lpass_cpu_comp_driver, 126362306a36Sopenharmony_ci variant->dai_driver, 126462306a36Sopenharmony_ci variant->num_dai); 126562306a36Sopenharmony_ci if (ret) { 126662306a36Sopenharmony_ci dev_err(dev, "error registering cpu driver: %d\n", ret); 126762306a36Sopenharmony_ci goto err; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci ret = asoc_qcom_lpass_platform_register(pdev); 127162306a36Sopenharmony_ci if (ret) { 127262306a36Sopenharmony_ci dev_err(dev, "error registering platform driver: %d\n", ret); 127362306a36Sopenharmony_ci goto err; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cierr: 127762306a36Sopenharmony_ci return ret; 127862306a36Sopenharmony_ci} 127962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ciint asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct lpass_data *drvdata = platform_get_drvdata(pdev); 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci if (drvdata->variant->exit) 128662306a36Sopenharmony_ci drvdata->variant->exit(pdev); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci return 0; 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_civoid asoc_qcom_lpass_cpu_platform_shutdown(struct platform_device *pdev) 129462306a36Sopenharmony_ci{ 129562306a36Sopenharmony_ci struct lpass_data *drvdata = platform_get_drvdata(pdev); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (drvdata->variant->exit) 129862306a36Sopenharmony_ci drvdata->variant->exit(pdev); 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci} 130162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_shutdown); 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ciMODULE_DESCRIPTION("QTi LPASS CPU Driver"); 130462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1305