| .. | .. | 
|---|
|  | 1 | +// SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 1 | 2 | /* | 
|---|
| 2 | 3 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | 
|---|
| 3 |  | - * | 
|---|
| 4 |  | - * This program is free software; you can redistribute it and/or modify | 
|---|
| 5 |  | - * it under the terms of the GNU General Public License version 2 and | 
|---|
| 6 |  | - * only version 2 as published by the Free Software Foundation. | 
|---|
| 7 |  | - * | 
|---|
| 8 |  | - * This program is distributed in the hope that it will be useful, | 
|---|
| 9 |  | - * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 10 |  | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 11 |  | - * GNU General Public License for more details. | 
|---|
| 12 | 4 | * | 
|---|
| 13 | 5 | * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS | 
|---|
| 14 | 6 | */ | 
|---|
| .. | .. | 
|---|
| 26 | 18 | #include <sound/soc-dai.h> | 
|---|
| 27 | 19 | #include "lpass-lpaif-reg.h" | 
|---|
| 28 | 20 | #include "lpass.h" | 
|---|
|  | 21 | + | 
|---|
|  | 22 | +#define LPASS_CPU_MAX_MI2S_LINES	4 | 
|---|
|  | 23 | +#define LPASS_CPU_I2S_SD0_MASK		BIT(0) | 
|---|
|  | 24 | +#define LPASS_CPU_I2S_SD1_MASK		BIT(1) | 
|---|
|  | 25 | +#define LPASS_CPU_I2S_SD2_MASK		BIT(2) | 
|---|
|  | 26 | +#define LPASS_CPU_I2S_SD3_MASK		BIT(3) | 
|---|
|  | 27 | +#define LPASS_CPU_I2S_SD0_1_MASK	GENMASK(1, 0) | 
|---|
|  | 28 | +#define LPASS_CPU_I2S_SD2_3_MASK	GENMASK(3, 2) | 
|---|
|  | 29 | +#define LPASS_CPU_I2S_SD0_1_2_MASK	GENMASK(2, 0) | 
|---|
|  | 30 | +#define LPASS_CPU_I2S_SD0_1_2_3_MASK	GENMASK(3, 0) | 
|---|
|  | 31 | + | 
|---|
|  | 32 | +static int lpass_cpu_init_i2sctl_bitfields(struct device *dev, | 
|---|
|  | 33 | +			struct lpaif_i2sctl *i2sctl, struct regmap *map) | 
|---|
|  | 34 | +{ | 
|---|
|  | 35 | +	struct lpass_data *drvdata = dev_get_drvdata(dev); | 
|---|
|  | 36 | +	struct lpass_variant *v = drvdata->variant; | 
|---|
|  | 37 | + | 
|---|
|  | 38 | +	i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback); | 
|---|
|  | 39 | +	i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken); | 
|---|
|  | 40 | +	i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode); | 
|---|
|  | 41 | +	i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono); | 
|---|
|  | 42 | +	i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen); | 
|---|
|  | 43 | +	i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode); | 
|---|
|  | 44 | +	i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono); | 
|---|
|  | 45 | +	i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc); | 
|---|
|  | 46 | +	i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth); | 
|---|
|  | 47 | + | 
|---|
|  | 48 | +	if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) || | 
|---|
|  | 49 | +	    IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) || | 
|---|
|  | 50 | +	    IS_ERR(i2sctl->micen) || IS_ERR(i2sctl->micmode) || | 
|---|
|  | 51 | +	    IS_ERR(i2sctl->micmono) || IS_ERR(i2sctl->wssrc) || | 
|---|
|  | 52 | +	    IS_ERR(i2sctl->bitwidth)) | 
|---|
|  | 53 | +		return -EINVAL; | 
|---|
|  | 54 | + | 
|---|
|  | 55 | +	return 0; | 
|---|
|  | 56 | +} | 
|---|
| 29 | 57 |  | 
|---|
| 30 | 58 | static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, | 
|---|
| 31 | 59 | unsigned int freq, int dir) | 
|---|
| .. | .. | 
|---|
| 52 | 80 | dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret); | 
|---|
| 53 | 81 | return ret; | 
|---|
| 54 | 82 | } | 
|---|
| 55 |  | - | 
|---|
| 56 |  | -	ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]); | 
|---|
|  | 83 | +	ret = clk_prepare(drvdata->mi2s_bit_clk[dai->driver->id]); | 
|---|
| 57 | 84 | if (ret) { | 
|---|
| 58 | 85 | dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); | 
|---|
| 59 | 86 | clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); | 
|---|
| 60 | 87 | return ret; | 
|---|
| 61 | 88 | } | 
|---|
| 62 |  | - | 
|---|
| 63 | 89 | return 0; | 
|---|
| 64 | 90 | } | 
|---|
| 65 | 91 |  | 
|---|
| .. | .. | 
|---|
| 67 | 93 | struct snd_soc_dai *dai) | 
|---|
| 68 | 94 | { | 
|---|
| 69 | 95 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | 
|---|
| 70 |  | - | 
|---|
| 71 |  | -	clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); | 
|---|
|  | 96 | +	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; | 
|---|
|  | 97 | +	unsigned int id = dai->driver->id; | 
|---|
| 72 | 98 |  | 
|---|
| 73 | 99 | clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); | 
|---|
|  | 100 | +	/* | 
|---|
|  | 101 | +	 * Ensure LRCLK is disabled even in device node validation. | 
|---|
|  | 102 | +	 * Will not impact if disabled in lpass_cpu_daiops_trigger() | 
|---|
|  | 103 | +	 * suspend. | 
|---|
|  | 104 | +	 */ | 
|---|
|  | 105 | +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
|---|
|  | 106 | +		regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_DISABLE); | 
|---|
|  | 107 | +	else | 
|---|
|  | 108 | +		regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_DISABLE); | 
|---|
|  | 109 | + | 
|---|
|  | 110 | +	/* | 
|---|
|  | 111 | +	 * BCLK may not be enabled if lpass_cpu_daiops_prepare is called before | 
|---|
|  | 112 | +	 * lpass_cpu_daiops_shutdown. It's paired with the clk_enable in | 
|---|
|  | 113 | +	 * lpass_cpu_daiops_prepare. | 
|---|
|  | 114 | +	 */ | 
|---|
|  | 115 | +	if (drvdata->mi2s_was_prepared[dai->driver->id]) { | 
|---|
|  | 116 | +		drvdata->mi2s_was_prepared[dai->driver->id] = false; | 
|---|
|  | 117 | +		clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); | 
|---|
|  | 118 | +	} | 
|---|
|  | 119 | + | 
|---|
|  | 120 | +	clk_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); | 
|---|
| 74 | 121 | } | 
|---|
| 75 | 122 |  | 
|---|
| 76 | 123 | static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, | 
|---|
| 77 | 124 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | 
|---|
| 78 | 125 | { | 
|---|
| 79 | 126 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | 
|---|
|  | 127 | +	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; | 
|---|
|  | 128 | +	unsigned int id = dai->driver->id; | 
|---|
| 80 | 129 | snd_pcm_format_t format = params_format(params); | 
|---|
| 81 | 130 | unsigned int channels = params_channels(params); | 
|---|
| 82 | 131 | unsigned int rate = params_rate(params); | 
|---|
|  | 132 | +	unsigned int mode; | 
|---|
| 83 | 133 | unsigned int regval; | 
|---|
| 84 | 134 | int bitwidth, ret; | 
|---|
| 85 | 135 |  | 
|---|
| .. | .. | 
|---|
| 89 | 139 | return bitwidth; | 
|---|
| 90 | 140 | } | 
|---|
| 91 | 141 |  | 
|---|
| 92 |  | -	regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | | 
|---|
| 93 |  | -			LPAIF_I2SCTL_WSSRC_INTERNAL; | 
|---|
|  | 142 | +	ret = regmap_fields_write(i2sctl->loopback, id, | 
|---|
|  | 143 | +				 LPAIF_I2SCTL_LOOPBACK_DISABLE); | 
|---|
|  | 144 | +	if (ret) { | 
|---|
|  | 145 | +		dev_err(dai->dev, "error updating loopback field: %d\n", ret); | 
|---|
|  | 146 | +		return ret; | 
|---|
|  | 147 | +	} | 
|---|
|  | 148 | + | 
|---|
|  | 149 | +	ret = regmap_fields_write(i2sctl->wssrc, id, | 
|---|
|  | 150 | +				 LPAIF_I2SCTL_WSSRC_INTERNAL); | 
|---|
|  | 151 | +	if (ret) { | 
|---|
|  | 152 | +		dev_err(dai->dev, "error updating wssrc field: %d\n", ret); | 
|---|
|  | 153 | +		return ret; | 
|---|
|  | 154 | +	} | 
|---|
| 94 | 155 |  | 
|---|
| 95 | 156 | switch (bitwidth) { | 
|---|
| 96 | 157 | case 16: | 
|---|
| 97 |  | -		regval |= LPAIF_I2SCTL_BITWIDTH_16; | 
|---|
|  | 158 | +		regval = LPAIF_I2SCTL_BITWIDTH_16; | 
|---|
| 98 | 159 | break; | 
|---|
| 99 | 160 | case 24: | 
|---|
| 100 |  | -		regval |= LPAIF_I2SCTL_BITWIDTH_24; | 
|---|
|  | 161 | +		regval = LPAIF_I2SCTL_BITWIDTH_24; | 
|---|
| 101 | 162 | break; | 
|---|
| 102 | 163 | case 32: | 
|---|
| 103 |  | -		regval |= LPAIF_I2SCTL_BITWIDTH_32; | 
|---|
|  | 164 | +		regval = LPAIF_I2SCTL_BITWIDTH_32; | 
|---|
| 104 | 165 | break; | 
|---|
| 105 | 166 | default: | 
|---|
| 106 | 167 | dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth); | 
|---|
| 107 | 168 | return -EINVAL; | 
|---|
| 108 | 169 | } | 
|---|
| 109 | 170 |  | 
|---|
| 110 |  | -	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
|---|
| 111 |  | -		switch (channels) { | 
|---|
| 112 |  | -		case 1: | 
|---|
| 113 |  | -			regval |= LPAIF_I2SCTL_SPKMODE_SD0; | 
|---|
| 114 |  | -			regval |= LPAIF_I2SCTL_SPKMONO_MONO; | 
|---|
| 115 |  | -			break; | 
|---|
| 116 |  | -		case 2: | 
|---|
| 117 |  | -			regval |= LPAIF_I2SCTL_SPKMODE_SD0; | 
|---|
| 118 |  | -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | 
|---|
| 119 |  | -			break; | 
|---|
| 120 |  | -		case 4: | 
|---|
| 121 |  | -			regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; | 
|---|
| 122 |  | -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | 
|---|
| 123 |  | -			break; | 
|---|
| 124 |  | -		case 6: | 
|---|
| 125 |  | -			regval |= LPAIF_I2SCTL_SPKMODE_6CH; | 
|---|
| 126 |  | -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | 
|---|
| 127 |  | -			break; | 
|---|
| 128 |  | -		case 8: | 
|---|
| 129 |  | -			regval |= LPAIF_I2SCTL_SPKMODE_8CH; | 
|---|
| 130 |  | -			regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | 
|---|
| 131 |  | -			break; | 
|---|
| 132 |  | -		default: | 
|---|
| 133 |  | -			dev_err(dai->dev, "invalid channels given: %u\n", | 
|---|
| 134 |  | -				channels); | 
|---|
| 135 |  | -			return -EINVAL; | 
|---|
| 136 |  | -		} | 
|---|
| 137 |  | -	} else { | 
|---|
| 138 |  | -		switch (channels) { | 
|---|
| 139 |  | -		case 1: | 
|---|
| 140 |  | -			regval |= LPAIF_I2SCTL_MICMODE_SD0; | 
|---|
| 141 |  | -			regval |= LPAIF_I2SCTL_MICMONO_MONO; | 
|---|
| 142 |  | -			break; | 
|---|
| 143 |  | -		case 2: | 
|---|
| 144 |  | -			regval |= LPAIF_I2SCTL_MICMODE_SD0; | 
|---|
| 145 |  | -			regval |= LPAIF_I2SCTL_MICMONO_STEREO; | 
|---|
| 146 |  | -			break; | 
|---|
| 147 |  | -		case 4: | 
|---|
| 148 |  | -			regval |= LPAIF_I2SCTL_MICMODE_QUAD01; | 
|---|
| 149 |  | -			regval |= LPAIF_I2SCTL_MICMONO_STEREO; | 
|---|
| 150 |  | -			break; | 
|---|
| 151 |  | -		case 6: | 
|---|
| 152 |  | -			regval |= LPAIF_I2SCTL_MICMODE_6CH; | 
|---|
| 153 |  | -			regval |= LPAIF_I2SCTL_MICMONO_STEREO; | 
|---|
| 154 |  | -			break; | 
|---|
| 155 |  | -		case 8: | 
|---|
| 156 |  | -			regval |= LPAIF_I2SCTL_MICMODE_8CH; | 
|---|
| 157 |  | -			regval |= LPAIF_I2SCTL_MICMONO_STEREO; | 
|---|
| 158 |  | -			break; | 
|---|
| 159 |  | -		default: | 
|---|
| 160 |  | -			dev_err(dai->dev, "invalid channels given: %u\n", | 
|---|
| 161 |  | -				channels); | 
|---|
| 162 |  | -			return -EINVAL; | 
|---|
| 163 |  | -		} | 
|---|
| 164 |  | -	} | 
|---|
| 165 |  | - | 
|---|
| 166 |  | -	ret = regmap_write(drvdata->lpaif_map, | 
|---|
| 167 |  | -			   LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), | 
|---|
| 168 |  | -			   regval); | 
|---|
|  | 171 | +	ret = regmap_fields_write(i2sctl->bitwidth, id, regval); | 
|---|
| 169 | 172 | if (ret) { | 
|---|
| 170 |  | -		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); | 
|---|
|  | 173 | +		dev_err(dai->dev, "error updating bitwidth field: %d\n", ret); | 
|---|
| 171 | 174 | return ret; | 
|---|
| 172 | 175 | } | 
|---|
| 173 | 176 |  | 
|---|
| 174 |  | -	ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id], | 
|---|
|  | 177 | +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
|---|
|  | 178 | +		mode = drvdata->mi2s_playback_sd_mode[id]; | 
|---|
|  | 179 | +	else | 
|---|
|  | 180 | +		mode = drvdata->mi2s_capture_sd_mode[id]; | 
|---|
|  | 181 | + | 
|---|
|  | 182 | +	if (!mode) { | 
|---|
|  | 183 | +		dev_err(dai->dev, "no line is assigned\n"); | 
|---|
|  | 184 | +		return -EINVAL; | 
|---|
|  | 185 | +	} | 
|---|
|  | 186 | + | 
|---|
|  | 187 | +	switch (channels) { | 
|---|
|  | 188 | +	case 1: | 
|---|
|  | 189 | +	case 2: | 
|---|
|  | 190 | +		switch (mode) { | 
|---|
|  | 191 | +		case LPAIF_I2SCTL_MODE_QUAD01: | 
|---|
|  | 192 | +		case LPAIF_I2SCTL_MODE_6CH: | 
|---|
|  | 193 | +		case LPAIF_I2SCTL_MODE_8CH: | 
|---|
|  | 194 | +			mode = LPAIF_I2SCTL_MODE_SD0; | 
|---|
|  | 195 | +			break; | 
|---|
|  | 196 | +		case LPAIF_I2SCTL_MODE_QUAD23: | 
|---|
|  | 197 | +			mode = LPAIF_I2SCTL_MODE_SD2; | 
|---|
|  | 198 | +			break; | 
|---|
|  | 199 | +		} | 
|---|
|  | 200 | + | 
|---|
|  | 201 | +		break; | 
|---|
|  | 202 | +	case 4: | 
|---|
|  | 203 | +		if (mode < LPAIF_I2SCTL_MODE_QUAD01) { | 
|---|
|  | 204 | +			dev_err(dai->dev, "cannot configure 4 channels with mode %d\n", | 
|---|
|  | 205 | +				mode); | 
|---|
|  | 206 | +			return -EINVAL; | 
|---|
|  | 207 | +		} | 
|---|
|  | 208 | + | 
|---|
|  | 209 | +		switch (mode) { | 
|---|
|  | 210 | +		case LPAIF_I2SCTL_MODE_6CH: | 
|---|
|  | 211 | +		case LPAIF_I2SCTL_MODE_8CH: | 
|---|
|  | 212 | +			mode = LPAIF_I2SCTL_MODE_QUAD01; | 
|---|
|  | 213 | +			break; | 
|---|
|  | 214 | +		} | 
|---|
|  | 215 | +		break; | 
|---|
|  | 216 | +	case 6: | 
|---|
|  | 217 | +		if (mode < LPAIF_I2SCTL_MODE_6CH) { | 
|---|
|  | 218 | +			dev_err(dai->dev, "cannot configure 6 channels with mode %d\n", | 
|---|
|  | 219 | +				mode); | 
|---|
|  | 220 | +			return -EINVAL; | 
|---|
|  | 221 | +		} | 
|---|
|  | 222 | + | 
|---|
|  | 223 | +		switch (mode) { | 
|---|
|  | 224 | +		case LPAIF_I2SCTL_MODE_8CH: | 
|---|
|  | 225 | +			mode = LPAIF_I2SCTL_MODE_6CH; | 
|---|
|  | 226 | +			break; | 
|---|
|  | 227 | +		} | 
|---|
|  | 228 | +		break; | 
|---|
|  | 229 | +	case 8: | 
|---|
|  | 230 | +		if (mode < LPAIF_I2SCTL_MODE_8CH) { | 
|---|
|  | 231 | +			dev_err(dai->dev, "cannot configure 8 channels with mode %d\n", | 
|---|
|  | 232 | +				mode); | 
|---|
|  | 233 | +			return -EINVAL; | 
|---|
|  | 234 | +		} | 
|---|
|  | 235 | +		break; | 
|---|
|  | 236 | +	default: | 
|---|
|  | 237 | +		dev_err(dai->dev, "invalid channels given: %u\n", channels); | 
|---|
|  | 238 | +		return -EINVAL; | 
|---|
|  | 239 | +	} | 
|---|
|  | 240 | + | 
|---|
|  | 241 | +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
|---|
|  | 242 | +		ret = regmap_fields_write(i2sctl->spkmode, id, | 
|---|
|  | 243 | +					 LPAIF_I2SCTL_SPKMODE(mode)); | 
|---|
|  | 244 | +		if (ret) { | 
|---|
|  | 245 | +			dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n", | 
|---|
|  | 246 | +				ret); | 
|---|
|  | 247 | +			return ret; | 
|---|
|  | 248 | +		} | 
|---|
|  | 249 | +		if (channels >= 2) | 
|---|
|  | 250 | +			ret = regmap_fields_write(i2sctl->spkmono, id, | 
|---|
|  | 251 | +						 LPAIF_I2SCTL_SPKMONO_STEREO); | 
|---|
|  | 252 | +		else | 
|---|
|  | 253 | +			ret = regmap_fields_write(i2sctl->spkmono, id, | 
|---|
|  | 254 | +						 LPAIF_I2SCTL_SPKMONO_MONO); | 
|---|
|  | 255 | +	} else { | 
|---|
|  | 256 | +		ret = regmap_fields_write(i2sctl->micmode, id, | 
|---|
|  | 257 | +					 LPAIF_I2SCTL_MICMODE(mode)); | 
|---|
|  | 258 | +		if (ret) { | 
|---|
|  | 259 | +			dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n", | 
|---|
|  | 260 | +				ret); | 
|---|
|  | 261 | +			return ret; | 
|---|
|  | 262 | +		} | 
|---|
|  | 263 | +		if (channels >= 2) | 
|---|
|  | 264 | +			ret = regmap_fields_write(i2sctl->micmono, id, | 
|---|
|  | 265 | +						 LPAIF_I2SCTL_MICMONO_STEREO); | 
|---|
|  | 266 | +		else | 
|---|
|  | 267 | +			ret = regmap_fields_write(i2sctl->micmono, id, | 
|---|
|  | 268 | +						 LPAIF_I2SCTL_MICMONO_MONO); | 
|---|
|  | 269 | +	} | 
|---|
|  | 270 | + | 
|---|
|  | 271 | +	if (ret) { | 
|---|
|  | 272 | +		dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n", | 
|---|
|  | 273 | +			ret); | 
|---|
|  | 274 | +		return ret; | 
|---|
|  | 275 | +	} | 
|---|
|  | 276 | + | 
|---|
|  | 277 | +	ret = clk_set_rate(drvdata->mi2s_bit_clk[id], | 
|---|
| 175 | 278 | rate * bitwidth * 2); | 
|---|
| 176 | 279 | if (ret) { | 
|---|
| 177 | 280 | dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n", | 
|---|
| .. | .. | 
|---|
| 182 | 285 | return 0; | 
|---|
| 183 | 286 | } | 
|---|
| 184 | 287 |  | 
|---|
| 185 |  | -static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, | 
|---|
| 186 |  | -		struct snd_soc_dai *dai) | 
|---|
| 187 |  | -{ | 
|---|
| 188 |  | -	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | 
|---|
| 189 |  | -	int ret; | 
|---|
| 190 |  | -	unsigned int val, mask; | 
|---|
| 191 |  | - | 
|---|
| 192 |  | -	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
|---|
| 193 |  | -		val = LPAIF_I2SCTL_SPKEN_ENABLE; | 
|---|
| 194 |  | -		mask = LPAIF_I2SCTL_SPKEN_MASK; | 
|---|
| 195 |  | -	} else  { | 
|---|
| 196 |  | -		val = LPAIF_I2SCTL_MICEN_ENABLE; | 
|---|
| 197 |  | -		mask = LPAIF_I2SCTL_MICEN_MASK; | 
|---|
| 198 |  | -	} | 
|---|
| 199 |  | - | 
|---|
| 200 |  | -	ret = regmap_update_bits(drvdata->lpaif_map, | 
|---|
| 201 |  | -			LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), | 
|---|
| 202 |  | -			mask, val); | 
|---|
| 203 |  | -	if (ret) | 
|---|
| 204 |  | -		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); | 
|---|
| 205 |  | - | 
|---|
| 206 |  | -	return ret; | 
|---|
| 207 |  | -} | 
|---|
| 208 |  | - | 
|---|
| 209 | 288 | static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, | 
|---|
| 210 | 289 | int cmd, struct snd_soc_dai *dai) | 
|---|
| 211 | 290 | { | 
|---|
| 212 | 291 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | 
|---|
|  | 292 | +	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; | 
|---|
|  | 293 | +	unsigned int id = dai->driver->id; | 
|---|
| 213 | 294 | int ret = -EINVAL; | 
|---|
| 214 |  | -	unsigned int val, mask; | 
|---|
| 215 | 295 |  | 
|---|
| 216 | 296 | switch (cmd) { | 
|---|
| 217 | 297 | case SNDRV_PCM_TRIGGER_START: | 
|---|
| 218 | 298 | case SNDRV_PCM_TRIGGER_RESUME: | 
|---|
| 219 | 299 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 
|---|
|  | 300 | +		/* | 
|---|
|  | 301 | +		 * Ensure lpass BCLK/LRCLK is enabled during | 
|---|
|  | 302 | +		 * device resume as lpass_cpu_daiops_prepare() is not called | 
|---|
|  | 303 | +		 * after the device resumes. We don't check mi2s_was_prepared before | 
|---|
|  | 304 | +		 * enable/disable BCLK in trigger events because: | 
|---|
|  | 305 | +		 *  1. These trigger events are paired, so the BCLK | 
|---|
|  | 306 | +		 *     enable_count is balanced. | 
|---|
|  | 307 | +		 *  2. the BCLK can be shared (ex: headset and headset mic), | 
|---|
|  | 308 | +		 *     we need to increase the enable_count so that we don't | 
|---|
|  | 309 | +		 *     turn off the shared BCLK while other devices are using | 
|---|
|  | 310 | +		 *     it. | 
|---|
|  | 311 | +		 */ | 
|---|
| 220 | 312 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
|---|
| 221 |  | -			val = LPAIF_I2SCTL_SPKEN_ENABLE; | 
|---|
| 222 |  | -			mask = LPAIF_I2SCTL_SPKEN_MASK; | 
|---|
|  | 313 | +			ret = regmap_fields_write(i2sctl->spken, id, | 
|---|
|  | 314 | +						 LPAIF_I2SCTL_SPKEN_ENABLE); | 
|---|
| 223 | 315 | } else  { | 
|---|
| 224 |  | -			val = LPAIF_I2SCTL_MICEN_ENABLE; | 
|---|
| 225 |  | -			mask = LPAIF_I2SCTL_MICEN_MASK; | 
|---|
|  | 316 | +			ret = regmap_fields_write(i2sctl->micen, id, | 
|---|
|  | 317 | +						 LPAIF_I2SCTL_MICEN_ENABLE); | 
|---|
| 226 | 318 | } | 
|---|
| 227 |  | - | 
|---|
| 228 |  | -		ret = regmap_update_bits(drvdata->lpaif_map, | 
|---|
| 229 |  | -				LPAIF_I2SCTL_REG(drvdata->variant, | 
|---|
| 230 |  | -						dai->driver->id), | 
|---|
| 231 |  | -				mask, val); | 
|---|
| 232 | 319 | if (ret) | 
|---|
| 233 | 320 | dev_err(dai->dev, "error writing to i2sctl reg: %d\n", | 
|---|
| 234 | 321 | ret); | 
|---|
|  | 322 | + | 
|---|
|  | 323 | +		ret = clk_enable(drvdata->mi2s_bit_clk[id]); | 
|---|
|  | 324 | +		if (ret) { | 
|---|
|  | 325 | +			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); | 
|---|
|  | 326 | +			clk_disable(drvdata->mi2s_osr_clk[id]); | 
|---|
|  | 327 | +			return ret; | 
|---|
|  | 328 | +		} | 
|---|
| 235 | 329 | break; | 
|---|
| 236 | 330 | case SNDRV_PCM_TRIGGER_STOP: | 
|---|
| 237 | 331 | case SNDRV_PCM_TRIGGER_SUSPEND: | 
|---|
| 238 | 332 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 
|---|
|  | 333 | +		/* | 
|---|
|  | 334 | +		 * To ensure lpass BCLK/LRCLK is disabled during | 
|---|
|  | 335 | +		 * device suspend. | 
|---|
|  | 336 | +		 */ | 
|---|
| 239 | 337 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 
|---|
| 240 |  | -			val = LPAIF_I2SCTL_SPKEN_DISABLE; | 
|---|
| 241 |  | -			mask = LPAIF_I2SCTL_SPKEN_MASK; | 
|---|
|  | 338 | +			ret = regmap_fields_write(i2sctl->spken, id, | 
|---|
|  | 339 | +						 LPAIF_I2SCTL_SPKEN_DISABLE); | 
|---|
| 242 | 340 | } else  { | 
|---|
| 243 |  | -			val = LPAIF_I2SCTL_MICEN_DISABLE; | 
|---|
| 244 |  | -			mask = LPAIF_I2SCTL_MICEN_MASK; | 
|---|
|  | 341 | +			ret = regmap_fields_write(i2sctl->micen, id, | 
|---|
|  | 342 | +						 LPAIF_I2SCTL_MICEN_DISABLE); | 
|---|
| 245 | 343 | } | 
|---|
| 246 |  | - | 
|---|
| 247 |  | -		ret = regmap_update_bits(drvdata->lpaif_map, | 
|---|
| 248 |  | -				LPAIF_I2SCTL_REG(drvdata->variant, | 
|---|
| 249 |  | -						dai->driver->id), | 
|---|
| 250 |  | -				mask, val); | 
|---|
| 251 | 344 | if (ret) | 
|---|
| 252 | 345 | dev_err(dai->dev, "error writing to i2sctl reg: %d\n", | 
|---|
| 253 | 346 | ret); | 
|---|
|  | 347 | + | 
|---|
|  | 348 | +		clk_disable(drvdata->mi2s_bit_clk[dai->driver->id]); | 
|---|
|  | 349 | + | 
|---|
| 254 | 350 | break; | 
|---|
| 255 | 351 | } | 
|---|
| 256 | 352 |  | 
|---|
| 257 | 353 | return ret; | 
|---|
|  | 354 | +} | 
|---|
|  | 355 | + | 
|---|
|  | 356 | +static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, | 
|---|
|  | 357 | +		struct snd_soc_dai *dai) | 
|---|
|  | 358 | +{ | 
|---|
|  | 359 | +	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | 
|---|
|  | 360 | +	struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; | 
|---|
|  | 361 | +	unsigned int id = dai->driver->id; | 
|---|
|  | 362 | +	int ret; | 
|---|
|  | 363 | + | 
|---|
|  | 364 | +	/* | 
|---|
|  | 365 | +	 * Ensure lpass BCLK/LRCLK is enabled bit before playback/capture | 
|---|
|  | 366 | +	 * data flow starts. This allows other codec to have some delay before | 
|---|
|  | 367 | +	 * the data flow. | 
|---|
|  | 368 | +	 * (ex: to drop start up pop noise before capture starts). | 
|---|
|  | 369 | +	 */ | 
|---|
|  | 370 | +	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 
|---|
|  | 371 | +		ret = regmap_fields_write(i2sctl->spken, id, LPAIF_I2SCTL_SPKEN_ENABLE); | 
|---|
|  | 372 | +	else | 
|---|
|  | 373 | +		ret = regmap_fields_write(i2sctl->micen, id, LPAIF_I2SCTL_MICEN_ENABLE); | 
|---|
|  | 374 | + | 
|---|
|  | 375 | +	if (ret) { | 
|---|
|  | 376 | +		dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); | 
|---|
|  | 377 | +		return ret; | 
|---|
|  | 378 | +	} | 
|---|
|  | 379 | + | 
|---|
|  | 380 | +	/* | 
|---|
|  | 381 | +	 * Check mi2s_was_prepared before enabling BCLK as lpass_cpu_daiops_prepare can | 
|---|
|  | 382 | +	 * be called multiple times. It's paired with the clk_disable in | 
|---|
|  | 383 | +	 * lpass_cpu_daiops_shutdown. | 
|---|
|  | 384 | +	 */ | 
|---|
|  | 385 | +	if (!drvdata->mi2s_was_prepared[dai->driver->id]) { | 
|---|
|  | 386 | +		ret = clk_enable(drvdata->mi2s_bit_clk[id]); | 
|---|
|  | 387 | +		if (ret) { | 
|---|
|  | 388 | +			dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); | 
|---|
|  | 389 | +			return ret; | 
|---|
|  | 390 | +		} | 
|---|
|  | 391 | +		drvdata->mi2s_was_prepared[dai->driver->id] = true; | 
|---|
|  | 392 | +	} | 
|---|
|  | 393 | +	return 0; | 
|---|
| 258 | 394 | } | 
|---|
| 259 | 395 |  | 
|---|
| 260 | 396 | const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { | 
|---|
| .. | .. | 
|---|
| 262 | 398 | .startup	= lpass_cpu_daiops_startup, | 
|---|
| 263 | 399 | .shutdown	= lpass_cpu_daiops_shutdown, | 
|---|
| 264 | 400 | .hw_params	= lpass_cpu_daiops_hw_params, | 
|---|
| 265 |  | -	.prepare	= lpass_cpu_daiops_prepare, | 
|---|
| 266 | 401 | .trigger	= lpass_cpu_daiops_trigger, | 
|---|
|  | 402 | +	.prepare	= lpass_cpu_daiops_prepare, | 
|---|
| 267 | 403 | }; | 
|---|
| 268 | 404 | EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); | 
|---|
| 269 | 405 |  | 
|---|
| .. | .. | 
|---|
| 282 | 418 | } | 
|---|
| 283 | 419 | EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); | 
|---|
| 284 | 420 |  | 
|---|
|  | 421 | +static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, | 
|---|
|  | 422 | +				   struct of_phandle_args *args, | 
|---|
|  | 423 | +				   const char **dai_name) | 
|---|
|  | 424 | +{ | 
|---|
|  | 425 | +	struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); | 
|---|
|  | 426 | +	struct lpass_variant *variant = drvdata->variant; | 
|---|
|  | 427 | +	int id = args->args[0]; | 
|---|
|  | 428 | +	int ret = -EINVAL; | 
|---|
|  | 429 | +	int i; | 
|---|
|  | 430 | + | 
|---|
|  | 431 | +	for (i = 0; i  < variant->num_dai; i++) { | 
|---|
|  | 432 | +		if (variant->dai_driver[i].id == id) { | 
|---|
|  | 433 | +			*dai_name = variant->dai_driver[i].name; | 
|---|
|  | 434 | +			ret = 0; | 
|---|
|  | 435 | +			break; | 
|---|
|  | 436 | +		} | 
|---|
|  | 437 | +	} | 
|---|
|  | 438 | + | 
|---|
|  | 439 | +	return ret; | 
|---|
|  | 440 | +} | 
|---|
|  | 441 | + | 
|---|
| 285 | 442 | static const struct snd_soc_component_driver lpass_cpu_comp_driver = { | 
|---|
| 286 | 443 | .name = "lpass-cpu", | 
|---|
|  | 444 | +	.of_xlate_dai_name = asoc_qcom_of_xlate_dai_name, | 
|---|
| 287 | 445 | }; | 
|---|
| 288 | 446 |  | 
|---|
| 289 | 447 | static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) | 
|---|
| .. | .. | 
|---|
| 405 | 563 | .cache_type = REGCACHE_FLAT, | 
|---|
| 406 | 564 | }; | 
|---|
| 407 | 565 |  | 
|---|
|  | 566 | +static int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map) | 
|---|
|  | 567 | +{ | 
|---|
|  | 568 | +	struct lpass_data *drvdata = dev_get_drvdata(dev); | 
|---|
|  | 569 | +	struct lpass_variant *v = drvdata->variant; | 
|---|
|  | 570 | +	unsigned int i; | 
|---|
|  | 571 | +	struct lpass_hdmi_tx_ctl *tx_ctl; | 
|---|
|  | 572 | +	struct regmap_field *legacy_en; | 
|---|
|  | 573 | +	struct lpass_vbit_ctrl *vbit_ctl; | 
|---|
|  | 574 | +	struct regmap_field *tx_parity; | 
|---|
|  | 575 | +	struct lpass_dp_metadata_ctl *meta_ctl; | 
|---|
|  | 576 | +	struct lpass_sstream_ctl *sstream_ctl; | 
|---|
|  | 577 | +	struct regmap_field *ch_msb; | 
|---|
|  | 578 | +	struct regmap_field *ch_lsb; | 
|---|
|  | 579 | +	struct lpass_hdmitx_dmactl *tx_dmactl; | 
|---|
|  | 580 | +	int rval; | 
|---|
|  | 581 | + | 
|---|
|  | 582 | +	tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL); | 
|---|
|  | 583 | +	if (!tx_ctl) | 
|---|
|  | 584 | +		return -ENOMEM; | 
|---|
|  | 585 | + | 
|---|
|  | 586 | +	QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset); | 
|---|
|  | 587 | +	QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset); | 
|---|
|  | 588 | +	drvdata->tx_ctl = tx_ctl; | 
|---|
|  | 589 | + | 
|---|
|  | 590 | +	QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en); | 
|---|
|  | 591 | +	drvdata->hdmitx_legacy_en = legacy_en; | 
|---|
|  | 592 | + | 
|---|
|  | 593 | +	vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL); | 
|---|
|  | 594 | +	if (!vbit_ctl) | 
|---|
|  | 595 | +		return -ENOMEM; | 
|---|
|  | 596 | + | 
|---|
|  | 597 | +	QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit); | 
|---|
|  | 598 | +	QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream); | 
|---|
|  | 599 | +	drvdata->vbit_ctl = vbit_ctl; | 
|---|
|  | 600 | + | 
|---|
|  | 601 | + | 
|---|
|  | 602 | +	QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity); | 
|---|
|  | 603 | +	drvdata->hdmitx_parity_calc_en = tx_parity; | 
|---|
|  | 604 | + | 
|---|
|  | 605 | +	meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL); | 
|---|
|  | 606 | +	if (!meta_ctl) | 
|---|
|  | 607 | +		return -ENOMEM; | 
|---|
|  | 608 | + | 
|---|
|  | 609 | +	rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7); | 
|---|
|  | 610 | +	if (rval) | 
|---|
|  | 611 | +		return rval; | 
|---|
|  | 612 | +	drvdata->meta_ctl = meta_ctl; | 
|---|
|  | 613 | + | 
|---|
|  | 614 | +	sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL); | 
|---|
|  | 615 | +	if (!sstream_ctl) | 
|---|
|  | 616 | +		return -ENOMEM; | 
|---|
|  | 617 | + | 
|---|
|  | 618 | +	rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9); | 
|---|
|  | 619 | +	if (rval) | 
|---|
|  | 620 | +		return rval; | 
|---|
|  | 621 | + | 
|---|
|  | 622 | +	drvdata->sstream_ctl = sstream_ctl; | 
|---|
|  | 623 | + | 
|---|
|  | 624 | +	for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) { | 
|---|
|  | 625 | +		QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb); | 
|---|
|  | 626 | +		drvdata->hdmitx_ch_msb[i] = ch_msb; | 
|---|
|  | 627 | + | 
|---|
|  | 628 | +		QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb); | 
|---|
|  | 629 | +		drvdata->hdmitx_ch_lsb[i] = ch_lsb; | 
|---|
|  | 630 | + | 
|---|
|  | 631 | +		tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL); | 
|---|
|  | 632 | +		if (!tx_dmactl) | 
|---|
|  | 633 | +			return -ENOMEM; | 
|---|
|  | 634 | + | 
|---|
|  | 635 | +		QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs); | 
|---|
|  | 636 | +		QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr); | 
|---|
|  | 637 | +		QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel); | 
|---|
|  | 638 | +		QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel); | 
|---|
|  | 639 | +		drvdata->hdmi_tx_dmactl[i] = tx_dmactl; | 
|---|
|  | 640 | +	} | 
|---|
|  | 641 | +	return 0; | 
|---|
|  | 642 | +} | 
|---|
|  | 643 | + | 
|---|
|  | 644 | +static bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg) | 
|---|
|  | 645 | +{ | 
|---|
|  | 646 | +	struct lpass_data *drvdata = dev_get_drvdata(dev); | 
|---|
|  | 647 | +	struct lpass_variant *v = drvdata->variant; | 
|---|
|  | 648 | +	int i; | 
|---|
|  | 649 | + | 
|---|
|  | 650 | +	if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) | 
|---|
|  | 651 | +		return true; | 
|---|
|  | 652 | +	if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) | 
|---|
|  | 653 | +		return true; | 
|---|
|  | 654 | +	if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) | 
|---|
|  | 655 | +		return true; | 
|---|
|  | 656 | +	if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) | 
|---|
|  | 657 | +		return true; | 
|---|
|  | 658 | +	if (reg == LPASS_HDMI_TX_DP_ADDR(v)) | 
|---|
|  | 659 | +		return true; | 
|---|
|  | 660 | +	if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) | 
|---|
|  | 661 | +		return true; | 
|---|
|  | 662 | +	if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) | 
|---|
|  | 663 | +		return true; | 
|---|
|  | 664 | +	if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v)) | 
|---|
|  | 665 | +		return true; | 
|---|
|  | 666 | + | 
|---|
|  | 667 | +	for (i = 0; i < v->hdmi_rdma_channels; i++) { | 
|---|
|  | 668 | +		if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) | 
|---|
|  | 669 | +			return true; | 
|---|
|  | 670 | +		if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) | 
|---|
|  | 671 | +			return true; | 
|---|
|  | 672 | +		if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) | 
|---|
|  | 673 | +			return true; | 
|---|
|  | 674 | +	} | 
|---|
|  | 675 | + | 
|---|
|  | 676 | +	for (i = 0; i < v->hdmi_rdma_channels; ++i) { | 
|---|
|  | 677 | +		if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) | 
|---|
|  | 678 | +			return true; | 
|---|
|  | 679 | +		if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) | 
|---|
|  | 680 | +			return true; | 
|---|
|  | 681 | +		if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) | 
|---|
|  | 682 | +			return true; | 
|---|
|  | 683 | +		if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) | 
|---|
|  | 684 | +			return true; | 
|---|
|  | 685 | +	} | 
|---|
|  | 686 | +	return false; | 
|---|
|  | 687 | +} | 
|---|
|  | 688 | + | 
|---|
|  | 689 | +static bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg) | 
|---|
|  | 690 | +{ | 
|---|
|  | 691 | +	struct lpass_data *drvdata = dev_get_drvdata(dev); | 
|---|
|  | 692 | +	struct lpass_variant *v = drvdata->variant; | 
|---|
|  | 693 | +	int i; | 
|---|
|  | 694 | + | 
|---|
|  | 695 | +	if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) | 
|---|
|  | 696 | +		return true; | 
|---|
|  | 697 | +	if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) | 
|---|
|  | 698 | +		return true; | 
|---|
|  | 699 | +	if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) | 
|---|
|  | 700 | +		return true; | 
|---|
|  | 701 | + | 
|---|
|  | 702 | +	for (i = 0; i < v->hdmi_rdma_channels; i++) { | 
|---|
|  | 703 | +		if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) | 
|---|
|  | 704 | +			return true; | 
|---|
|  | 705 | +		if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) | 
|---|
|  | 706 | +			return true; | 
|---|
|  | 707 | +		if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) | 
|---|
|  | 708 | +			return true; | 
|---|
|  | 709 | +	} | 
|---|
|  | 710 | + | 
|---|
|  | 711 | +	if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) | 
|---|
|  | 712 | +		return true; | 
|---|
|  | 713 | +	if (reg == LPASS_HDMI_TX_DP_ADDR(v)) | 
|---|
|  | 714 | +		return true; | 
|---|
|  | 715 | +	if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) | 
|---|
|  | 716 | +		return true; | 
|---|
|  | 717 | +	if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) | 
|---|
|  | 718 | +		return true; | 
|---|
|  | 719 | +	if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) | 
|---|
|  | 720 | +		return true; | 
|---|
|  | 721 | + | 
|---|
|  | 722 | +	for (i = 0; i < v->hdmi_rdma_channels; ++i) { | 
|---|
|  | 723 | +		if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) | 
|---|
|  | 724 | +			return true; | 
|---|
|  | 725 | +		if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) | 
|---|
|  | 726 | +			return true; | 
|---|
|  | 727 | +		if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) | 
|---|
|  | 728 | +			return true; | 
|---|
|  | 729 | +		if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) | 
|---|
|  | 730 | +			return true; | 
|---|
|  | 731 | +		if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) | 
|---|
|  | 732 | +			return true; | 
|---|
|  | 733 | +	} | 
|---|
|  | 734 | + | 
|---|
|  | 735 | +	return false; | 
|---|
|  | 736 | +} | 
|---|
|  | 737 | + | 
|---|
|  | 738 | +static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg) | 
|---|
|  | 739 | +{ | 
|---|
|  | 740 | +	struct lpass_data *drvdata = dev_get_drvdata(dev); | 
|---|
|  | 741 | +	struct lpass_variant *v = drvdata->variant; | 
|---|
|  | 742 | +	int i; | 
|---|
|  | 743 | + | 
|---|
|  | 744 | +	if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) | 
|---|
|  | 745 | +		return true; | 
|---|
|  | 746 | +	if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) | 
|---|
|  | 747 | +		return true; | 
|---|
|  | 748 | +	if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) | 
|---|
|  | 749 | +		return true; | 
|---|
|  | 750 | +	if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) | 
|---|
|  | 751 | +		return true; | 
|---|
|  | 752 | + | 
|---|
|  | 753 | +	for (i = 0; i < v->hdmi_rdma_channels; ++i) { | 
|---|
|  | 754 | +		if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) | 
|---|
|  | 755 | +			return true; | 
|---|
|  | 756 | +		if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) | 
|---|
|  | 757 | +			return true; | 
|---|
|  | 758 | +		if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) | 
|---|
|  | 759 | +			return true; | 
|---|
|  | 760 | +		if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) | 
|---|
|  | 761 | +			return true; | 
|---|
|  | 762 | +	} | 
|---|
|  | 763 | +	return false; | 
|---|
|  | 764 | +} | 
|---|
|  | 765 | + | 
|---|
|  | 766 | +struct regmap_config lpass_hdmi_regmap_config = { | 
|---|
|  | 767 | +	.reg_bits = 32, | 
|---|
|  | 768 | +	.reg_stride = 4, | 
|---|
|  | 769 | +	.val_bits = 32, | 
|---|
|  | 770 | +	.writeable_reg = lpass_hdmi_regmap_writeable, | 
|---|
|  | 771 | +	.readable_reg = lpass_hdmi_regmap_readable, | 
|---|
|  | 772 | +	.volatile_reg = lpass_hdmi_regmap_volatile, | 
|---|
|  | 773 | +	.cache_type = REGCACHE_FLAT, | 
|---|
|  | 774 | +}; | 
|---|
|  | 775 | + | 
|---|
|  | 776 | +static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, | 
|---|
|  | 777 | +						struct device_node *node, | 
|---|
|  | 778 | +						const char *name) | 
|---|
|  | 779 | +{ | 
|---|
|  | 780 | +	unsigned int lines[LPASS_CPU_MAX_MI2S_LINES]; | 
|---|
|  | 781 | +	unsigned int sd_line_mask = 0; | 
|---|
|  | 782 | +	int num_lines, i; | 
|---|
|  | 783 | + | 
|---|
|  | 784 | +	num_lines = of_property_read_variable_u32_array(node, name, lines, 0, | 
|---|
|  | 785 | +							LPASS_CPU_MAX_MI2S_LINES); | 
|---|
|  | 786 | +	if (num_lines < 0) | 
|---|
|  | 787 | +		return LPAIF_I2SCTL_MODE_NONE; | 
|---|
|  | 788 | + | 
|---|
|  | 789 | +	for (i = 0; i < num_lines; i++) | 
|---|
|  | 790 | +		sd_line_mask |= BIT(lines[i]); | 
|---|
|  | 791 | + | 
|---|
|  | 792 | +	switch (sd_line_mask) { | 
|---|
|  | 793 | +	case LPASS_CPU_I2S_SD0_MASK: | 
|---|
|  | 794 | +		return LPAIF_I2SCTL_MODE_SD0; | 
|---|
|  | 795 | +	case LPASS_CPU_I2S_SD1_MASK: | 
|---|
|  | 796 | +		return LPAIF_I2SCTL_MODE_SD1; | 
|---|
|  | 797 | +	case LPASS_CPU_I2S_SD2_MASK: | 
|---|
|  | 798 | +		return LPAIF_I2SCTL_MODE_SD2; | 
|---|
|  | 799 | +	case LPASS_CPU_I2S_SD3_MASK: | 
|---|
|  | 800 | +		return LPAIF_I2SCTL_MODE_SD3; | 
|---|
|  | 801 | +	case LPASS_CPU_I2S_SD0_1_MASK: | 
|---|
|  | 802 | +		return LPAIF_I2SCTL_MODE_QUAD01; | 
|---|
|  | 803 | +	case LPASS_CPU_I2S_SD2_3_MASK: | 
|---|
|  | 804 | +		return LPAIF_I2SCTL_MODE_QUAD23; | 
|---|
|  | 805 | +	case LPASS_CPU_I2S_SD0_1_2_MASK: | 
|---|
|  | 806 | +		return LPAIF_I2SCTL_MODE_6CH; | 
|---|
|  | 807 | +	case LPASS_CPU_I2S_SD0_1_2_3_MASK: | 
|---|
|  | 808 | +		return LPAIF_I2SCTL_MODE_8CH; | 
|---|
|  | 809 | +	default: | 
|---|
|  | 810 | +		dev_err(dev, "Unsupported SD line mask: %#x\n", sd_line_mask); | 
|---|
|  | 811 | +		return LPAIF_I2SCTL_MODE_NONE; | 
|---|
|  | 812 | +	} | 
|---|
|  | 813 | +} | 
|---|
|  | 814 | + | 
|---|
|  | 815 | +static void of_lpass_cpu_parse_dai_data(struct device *dev, | 
|---|
|  | 816 | +					struct lpass_data *data) | 
|---|
|  | 817 | +{ | 
|---|
|  | 818 | +	struct device_node *node; | 
|---|
|  | 819 | +	int ret, id; | 
|---|
|  | 820 | + | 
|---|
|  | 821 | +	/* Allow all channels by default for backwards compatibility */ | 
|---|
|  | 822 | +	for (id = 0; id < data->variant->num_dai; id++) { | 
|---|
|  | 823 | +		data->mi2s_playback_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; | 
|---|
|  | 824 | +		data->mi2s_capture_sd_mode[id] = LPAIF_I2SCTL_MODE_8CH; | 
|---|
|  | 825 | +	} | 
|---|
|  | 826 | + | 
|---|
|  | 827 | +	for_each_child_of_node(dev->of_node, node) { | 
|---|
|  | 828 | +		ret = of_property_read_u32(node, "reg", &id); | 
|---|
|  | 829 | +		if (ret || id < 0) { | 
|---|
|  | 830 | +			dev_err(dev, "valid dai id not found: %d\n", ret); | 
|---|
|  | 831 | +			continue; | 
|---|
|  | 832 | +		} | 
|---|
|  | 833 | +		if (id == LPASS_DP_RX) { | 
|---|
|  | 834 | +			data->hdmi_port_enable = 1; | 
|---|
|  | 835 | +		} else { | 
|---|
|  | 836 | +			data->mi2s_playback_sd_mode[id] = | 
|---|
|  | 837 | +				of_lpass_cpu_parse_sd_lines(dev, node, | 
|---|
|  | 838 | +							    "qcom,playback-sd-lines"); | 
|---|
|  | 839 | +			data->mi2s_capture_sd_mode[id] = | 
|---|
|  | 840 | +				of_lpass_cpu_parse_sd_lines(dev, node, | 
|---|
|  | 841 | +						    "qcom,capture-sd-lines"); | 
|---|
|  | 842 | +		} | 
|---|
|  | 843 | +	} | 
|---|
|  | 844 | +} | 
|---|
|  | 845 | + | 
|---|
| 408 | 846 | int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) | 
|---|
| 409 | 847 | { | 
|---|
| 410 | 848 | struct lpass_data *drvdata; | 
|---|
| .. | .. | 
|---|
| 417 | 855 |  | 
|---|
| 418 | 856 | dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); | 
|---|
| 419 | 857 | if (dsp_of_node) { | 
|---|
| 420 |  | -		dev_err(&pdev->dev, "DSP exists and holds audio resources\n"); | 
|---|
|  | 858 | +		dev_err(dev, "DSP exists and holds audio resources\n"); | 
|---|
|  | 859 | +		of_node_put(dsp_of_node); | 
|---|
| 421 | 860 | return -EBUSY; | 
|---|
| 422 | 861 | } | 
|---|
| 423 | 862 |  | 
|---|
| 424 |  | -	drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data), | 
|---|
| 425 |  | -			GFP_KERNEL); | 
|---|
|  | 863 | +	drvdata = devm_kzalloc(dev, sizeof(struct lpass_data), GFP_KERNEL); | 
|---|
| 426 | 864 | if (!drvdata) | 
|---|
| 427 | 865 | return -ENOMEM; | 
|---|
| 428 | 866 | platform_set_drvdata(pdev, drvdata); | 
|---|
| .. | .. | 
|---|
| 434 | 872 | drvdata->variant = (struct lpass_variant *)match->data; | 
|---|
| 435 | 873 | variant = drvdata->variant; | 
|---|
| 436 | 874 |  | 
|---|
|  | 875 | +	of_lpass_cpu_parse_dai_data(dev, drvdata); | 
|---|
|  | 876 | + | 
|---|
| 437 | 877 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); | 
|---|
| 438 | 878 |  | 
|---|
| 439 |  | -	drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); | 
|---|
|  | 879 | +	drvdata->lpaif = devm_ioremap_resource(dev, res); | 
|---|
| 440 | 880 | if (IS_ERR((void const __force *)drvdata->lpaif)) { | 
|---|
| 441 |  | -		dev_err(&pdev->dev, "error mapping reg resource: %ld\n", | 
|---|
|  | 881 | +		dev_err(dev, "error mapping reg resource: %ld\n", | 
|---|
| 442 | 882 | PTR_ERR((void const __force *)drvdata->lpaif)); | 
|---|
| 443 | 883 | return PTR_ERR((void const __force *)drvdata->lpaif); | 
|---|
| 444 | 884 | } | 
|---|
| .. | .. | 
|---|
| 447 | 887 | variant->wrdma_channels + | 
|---|
| 448 | 888 | variant->wrdma_channel_start); | 
|---|
| 449 | 889 |  | 
|---|
| 450 |  | -	drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, | 
|---|
|  | 890 | +	drvdata->lpaif_map = devm_regmap_init_mmio(dev, drvdata->lpaif, | 
|---|
| 451 | 891 | &lpass_cpu_regmap_config); | 
|---|
| 452 | 892 | if (IS_ERR(drvdata->lpaif_map)) { | 
|---|
| 453 |  | -		dev_err(&pdev->dev, "error initializing regmap: %ld\n", | 
|---|
|  | 893 | +		dev_err(dev, "error initializing regmap: %ld\n", | 
|---|
| 454 | 894 | PTR_ERR(drvdata->lpaif_map)); | 
|---|
| 455 | 895 | return PTR_ERR(drvdata->lpaif_map); | 
|---|
| 456 | 896 | } | 
|---|
| 457 | 897 |  | 
|---|
| 458 |  | -	if (variant->init) | 
|---|
| 459 |  | -		variant->init(pdev); | 
|---|
|  | 898 | +	if (drvdata->hdmi_port_enable) { | 
|---|
|  | 899 | +		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-hdmiif"); | 
|---|
|  | 900 | + | 
|---|
|  | 901 | +		drvdata->hdmiif = devm_ioremap_resource(dev, res); | 
|---|
|  | 902 | +		if (IS_ERR((void const __force *)drvdata->hdmiif)) { | 
|---|
|  | 903 | +			dev_err(dev, "error mapping reg resource: %ld\n", | 
|---|
|  | 904 | +					PTR_ERR((void const __force *)drvdata->hdmiif)); | 
|---|
|  | 905 | +			return PTR_ERR((void const __force *)drvdata->hdmiif); | 
|---|
|  | 906 | +		} | 
|---|
|  | 907 | + | 
|---|
|  | 908 | +		lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant, | 
|---|
|  | 909 | +					variant->hdmi_rdma_channels - 1); | 
|---|
|  | 910 | +		drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif, | 
|---|
|  | 911 | +					&lpass_hdmi_regmap_config); | 
|---|
|  | 912 | +		if (IS_ERR(drvdata->hdmiif_map)) { | 
|---|
|  | 913 | +			dev_err(dev, "error initializing regmap: %ld\n", | 
|---|
|  | 914 | +			PTR_ERR(drvdata->hdmiif_map)); | 
|---|
|  | 915 | +			return PTR_ERR(drvdata->hdmiif_map); | 
|---|
|  | 916 | +		} | 
|---|
|  | 917 | +	} | 
|---|
|  | 918 | + | 
|---|
|  | 919 | +	if (variant->init) { | 
|---|
|  | 920 | +		ret = variant->init(pdev); | 
|---|
|  | 921 | +		if (ret) { | 
|---|
|  | 922 | +			dev_err(dev, "error initializing variant: %d\n", ret); | 
|---|
|  | 923 | +			return ret; | 
|---|
|  | 924 | +		} | 
|---|
|  | 925 | +	} | 
|---|
| 460 | 926 |  | 
|---|
| 461 | 927 | for (i = 0; i < variant->num_dai; i++) { | 
|---|
| 462 | 928 | dai_id = variant->dai_driver[i].id; | 
|---|
| 463 |  | -		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev, | 
|---|
|  | 929 | +		if (dai_id == LPASS_DP_RX) | 
|---|
|  | 930 | +			continue; | 
|---|
|  | 931 | + | 
|---|
|  | 932 | +		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev, | 
|---|
| 464 | 933 | variant->dai_osr_clk_names[i]); | 
|---|
| 465 |  | -		if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { | 
|---|
| 466 |  | -			dev_warn(&pdev->dev, | 
|---|
| 467 |  | -				"%s() error getting optional %s: %ld\n", | 
|---|
| 468 |  | -				__func__, | 
|---|
| 469 |  | -				variant->dai_osr_clk_names[i], | 
|---|
| 470 |  | -				PTR_ERR(drvdata->mi2s_osr_clk[dai_id])); | 
|---|
| 471 |  | - | 
|---|
| 472 |  | -			drvdata->mi2s_osr_clk[dai_id] = NULL; | 
|---|
| 473 |  | -		} | 
|---|
| 474 |  | - | 
|---|
| 475 |  | -		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev, | 
|---|
|  | 934 | +		drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(dev, | 
|---|
| 476 | 935 | variant->dai_bit_clk_names[i]); | 
|---|
| 477 | 936 | if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { | 
|---|
| 478 |  | -			dev_err(&pdev->dev, | 
|---|
|  | 937 | +			dev_err(dev, | 
|---|
| 479 | 938 | "error getting %s: %ld\n", | 
|---|
| 480 | 939 | variant->dai_bit_clk_names[i], | 
|---|
| 481 | 940 | PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); | 
|---|
| .. | .. | 
|---|
| 483 | 942 | } | 
|---|
| 484 | 943 | } | 
|---|
| 485 | 944 |  | 
|---|
| 486 |  | -	drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); | 
|---|
| 487 |  | -	if (IS_ERR(drvdata->ahbix_clk)) { | 
|---|
| 488 |  | -		dev_err(&pdev->dev, "error getting ahbix-clk: %ld\n", | 
|---|
| 489 |  | -			PTR_ERR(drvdata->ahbix_clk)); | 
|---|
| 490 |  | -		return PTR_ERR(drvdata->ahbix_clk); | 
|---|
| 491 |  | -	} | 
|---|
|  | 945 | +	/* Allocation for i2sctl regmap fields */ | 
|---|
|  | 946 | +	drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl), | 
|---|
|  | 947 | +					GFP_KERNEL); | 
|---|
| 492 | 948 |  | 
|---|
| 493 |  | -	ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); | 
|---|
|  | 949 | +	/* Initialize bitfields for dai I2SCTL register */ | 
|---|
|  | 950 | +	ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl, | 
|---|
|  | 951 | +						drvdata->lpaif_map); | 
|---|
| 494 | 952 | if (ret) { | 
|---|
| 495 |  | -		dev_err(&pdev->dev, "error setting rate on ahbix_clk: %d\n", | 
|---|
| 496 |  | -			ret); | 
|---|
| 497 |  | -		return ret; | 
|---|
| 498 |  | -	} | 
|---|
| 499 |  | -	dev_dbg(&pdev->dev, "set ahbix_clk rate to %lu\n", | 
|---|
| 500 |  | -		clk_get_rate(drvdata->ahbix_clk)); | 
|---|
| 501 |  | - | 
|---|
| 502 |  | -	ret = clk_prepare_enable(drvdata->ahbix_clk); | 
|---|
| 503 |  | -	if (ret) { | 
|---|
| 504 |  | -		dev_err(&pdev->dev, "error enabling ahbix_clk: %d\n", ret); | 
|---|
|  | 953 | +		dev_err(dev, "error init i2sctl field: %d\n", ret); | 
|---|
| 505 | 954 | return ret; | 
|---|
| 506 | 955 | } | 
|---|
| 507 | 956 |  | 
|---|
| 508 |  | -	ret = devm_snd_soc_register_component(&pdev->dev, | 
|---|
|  | 957 | +	if (drvdata->hdmi_port_enable) { | 
|---|
|  | 958 | +		ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map); | 
|---|
|  | 959 | +		if (ret) { | 
|---|
|  | 960 | +			dev_err(dev, "%s error  hdmi init failed\n", __func__); | 
|---|
|  | 961 | +			return ret; | 
|---|
|  | 962 | +		} | 
|---|
|  | 963 | +	} | 
|---|
|  | 964 | +	ret = devm_snd_soc_register_component(dev, | 
|---|
| 509 | 965 | &lpass_cpu_comp_driver, | 
|---|
| 510 | 966 | variant->dai_driver, | 
|---|
| 511 | 967 | variant->num_dai); | 
|---|
| 512 | 968 | if (ret) { | 
|---|
| 513 |  | -		dev_err(&pdev->dev, "error registering cpu driver: %d\n", ret); | 
|---|
| 514 |  | -		goto err_clk; | 
|---|
|  | 969 | +		dev_err(dev, "error registering cpu driver: %d\n", ret); | 
|---|
|  | 970 | +		goto err; | 
|---|
| 515 | 971 | } | 
|---|
| 516 | 972 |  | 
|---|
| 517 | 973 | ret = asoc_qcom_lpass_platform_register(pdev); | 
|---|
| 518 | 974 | if (ret) { | 
|---|
| 519 |  | -		dev_err(&pdev->dev, "error registering platform driver: %d\n", | 
|---|
| 520 |  | -			ret); | 
|---|
| 521 |  | -		goto err_clk; | 
|---|
|  | 975 | +		dev_err(dev, "error registering platform driver: %d\n", ret); | 
|---|
|  | 976 | +		goto err; | 
|---|
| 522 | 977 | } | 
|---|
| 523 | 978 |  | 
|---|
| 524 |  | -	return 0; | 
|---|
| 525 |  | - | 
|---|
| 526 |  | -err_clk: | 
|---|
| 527 |  | -	clk_disable_unprepare(drvdata->ahbix_clk); | 
|---|
|  | 979 | +err: | 
|---|
| 528 | 980 | return ret; | 
|---|
| 529 | 981 | } | 
|---|
| 530 | 982 | EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); | 
|---|
| .. | .. | 
|---|
| 536 | 988 | if (drvdata->variant->exit) | 
|---|
| 537 | 989 | drvdata->variant->exit(pdev); | 
|---|
| 538 | 990 |  | 
|---|
| 539 |  | -	clk_disable_unprepare(drvdata->ahbix_clk); | 
|---|
| 540 | 991 |  | 
|---|
| 541 | 992 | return 0; | 
|---|
| 542 | 993 | } | 
|---|