1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Driver for ChromeOS Embedded Controller codec. 4 * 5 * This driver uses the cros-ec interface to communicate with the ChromeOS 6 * EC for audio function. 7 */ 8 9 #include <linux/delay.h> 10 #include <linux/device.h> 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/platform_data/cros_ec_commands.h> 14 #include <linux/platform_data/cros_ec_proto.h> 15 #include <linux/platform_device.h> 16 #include <sound/pcm.h> 17 #include <sound/pcm_params.h> 18 #include <sound/soc.h> 19 #include <sound/tlv.h> 20 21 #define DRV_NAME "cros-ec-codec" 22 23 /** 24 * struct cros_ec_codec_data - ChromeOS EC codec driver data. 25 * @dev: Device structure used in sysfs. 26 * @ec_device: cros_ec_device structure to talk to the physical device. 27 * @component: Pointer to the component. 28 * @max_dmic_gain: Maximum gain in dB supported by EC codec. 29 */ 30 struct cros_ec_codec_data { 31 struct device *dev; 32 struct cros_ec_device *ec_device; 33 struct snd_soc_component *component; 34 unsigned int max_dmic_gain; 35 }; 36 37 static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); 38 39 static int ec_command_get_gain(struct snd_soc_component *component, 40 struct ec_param_codec_i2s *param, 41 struct ec_codec_i2s_gain *resp) 42 { 43 struct cros_ec_codec_data *codec_data = 44 snd_soc_component_get_drvdata(component); 45 struct cros_ec_device *ec_device = codec_data->ec_device; 46 u8 buffer[sizeof(struct cros_ec_command) + 47 max(sizeof(struct ec_param_codec_i2s), 48 sizeof(struct ec_codec_i2s_gain))]; 49 struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; 50 int ret; 51 52 msg->version = 0; 53 msg->command = EC_CMD_CODEC_I2S; 54 msg->outsize = sizeof(struct ec_param_codec_i2s); 55 msg->insize = sizeof(struct ec_codec_i2s_gain); 56 57 memcpy(msg->data, param, msg->outsize); 58 59 ret = cros_ec_cmd_xfer_status(ec_device, msg); 60 if (ret > 0) 61 memcpy(resp, msg->data, msg->insize); 62 63 return ret; 64 } 65 66 /* 67 * Wrapper for EC command without response. 68 */ 69 static int ec_command_no_resp(struct snd_soc_component *component, 70 struct ec_param_codec_i2s *param) 71 { 72 struct cros_ec_codec_data *codec_data = 73 snd_soc_component_get_drvdata(component); 74 struct cros_ec_device *ec_device = codec_data->ec_device; 75 u8 buffer[sizeof(struct cros_ec_command) + 76 sizeof(struct ec_param_codec_i2s)]; 77 struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; 78 79 msg->version = 0; 80 msg->command = EC_CMD_CODEC_I2S; 81 msg->outsize = sizeof(struct ec_param_codec_i2s); 82 msg->insize = 0; 83 84 memcpy(msg->data, param, msg->outsize); 85 86 return cros_ec_cmd_xfer_status(ec_device, msg); 87 } 88 89 static int set_i2s_config(struct snd_soc_component *component, 90 enum ec_i2s_config i2s_config) 91 { 92 struct ec_param_codec_i2s param; 93 94 dev_dbg(component->dev, "%s set I2S format to %u\n", __func__, 95 i2s_config); 96 97 param.cmd = EC_CODEC_I2S_SET_CONFIG; 98 param.i2s_config = i2s_config; 99 100 return ec_command_no_resp(component, ¶m); 101 } 102 103 static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 104 { 105 struct snd_soc_component *component = dai->component; 106 enum ec_i2s_config i2s_config; 107 108 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 109 case SND_SOC_DAIFMT_CBS_CFS: 110 break; 111 default: 112 return -EINVAL; 113 } 114 115 switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 116 case SND_SOC_DAIFMT_NB_NF: 117 break; 118 default: 119 return -EINVAL; 120 } 121 122 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 123 case SND_SOC_DAIFMT_I2S: 124 i2s_config = EC_DAI_FMT_I2S; 125 break; 126 127 case SND_SOC_DAIFMT_RIGHT_J: 128 i2s_config = EC_DAI_FMT_RIGHT_J; 129 break; 130 131 case SND_SOC_DAIFMT_LEFT_J: 132 i2s_config = EC_DAI_FMT_LEFT_J; 133 break; 134 135 case SND_SOC_DAIFMT_DSP_A: 136 i2s_config = EC_DAI_FMT_PCM_A; 137 break; 138 139 case SND_SOC_DAIFMT_DSP_B: 140 i2s_config = EC_DAI_FMT_PCM_B; 141 break; 142 143 default: 144 return -EINVAL; 145 } 146 147 return set_i2s_config(component, i2s_config); 148 } 149 150 static int set_i2s_sample_depth(struct snd_soc_component *component, 151 enum ec_sample_depth_value depth) 152 { 153 struct ec_param_codec_i2s param; 154 155 dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth); 156 157 param.cmd = EC_CODEC_SET_SAMPLE_DEPTH; 158 param.depth = depth; 159 160 return ec_command_no_resp(component, ¶m); 161 } 162 163 static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) 164 { 165 struct ec_param_codec_i2s param; 166 167 dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk); 168 169 param.cmd = EC_CODEC_I2S_SET_BCLK; 170 param.bclk = bclk; 171 172 return ec_command_no_resp(component, ¶m); 173 } 174 175 static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream, 176 struct snd_pcm_hw_params *params, 177 struct snd_soc_dai *dai) 178 { 179 struct snd_soc_component *component = dai->component; 180 unsigned int rate, bclk; 181 int ret; 182 183 rate = params_rate(params); 184 if (rate != 48000) 185 return -EINVAL; 186 187 switch (params_format(params)) { 188 case SNDRV_PCM_FORMAT_S16_LE: 189 ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16); 190 break; 191 case SNDRV_PCM_FORMAT_S24_LE: 192 ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24); 193 break; 194 default: 195 return -EINVAL; 196 } 197 if (ret < 0) 198 return ret; 199 200 bclk = snd_soc_params_to_bclk(params); 201 return set_i2s_bclk(component, bclk); 202 } 203 204 static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { 205 .hw_params = cros_ec_i2s_hw_params, 206 .set_fmt = cros_ec_i2s_set_dai_fmt, 207 }; 208 209 static struct snd_soc_dai_driver cros_ec_dai[] = { 210 { 211 .name = "cros_ec_codec I2S", 212 .id = 0, 213 .capture = { 214 .stream_name = "I2S Capture", 215 .channels_min = 2, 216 .channels_max = 2, 217 .rates = SNDRV_PCM_RATE_48000, 218 .formats = SNDRV_PCM_FMTBIT_S16_LE | 219 SNDRV_PCM_FMTBIT_S24_LE, 220 }, 221 .ops = &cros_ec_i2s_dai_ops, 222 } 223 }; 224 225 static int get_ec_mic_gain(struct snd_soc_component *component, 226 u8 *left, u8 *right) 227 { 228 struct ec_param_codec_i2s param; 229 struct ec_codec_i2s_gain resp; 230 int ret; 231 232 param.cmd = EC_CODEC_GET_GAIN; 233 234 ret = ec_command_get_gain(component, ¶m, &resp); 235 if (ret < 0) 236 return ret; 237 238 *left = resp.left; 239 *right = resp.right; 240 241 return 0; 242 } 243 244 static int mic_gain_get(struct snd_kcontrol *kcontrol, 245 struct snd_ctl_elem_value *ucontrol) 246 { 247 struct snd_soc_component *component = 248 snd_soc_kcontrol_component(kcontrol); 249 u8 left, right; 250 int ret; 251 252 ret = get_ec_mic_gain(component, &left, &right); 253 if (ret) 254 return ret; 255 256 ucontrol->value.integer.value[0] = left; 257 ucontrol->value.integer.value[1] = right; 258 259 return 0; 260 } 261 262 static int set_ec_mic_gain(struct snd_soc_component *component, 263 u8 left, u8 right) 264 { 265 struct ec_param_codec_i2s param; 266 267 dev_dbg(component->dev, "%s set mic gain to %u, %u\n", 268 __func__, left, right); 269 270 param.cmd = EC_CODEC_SET_GAIN; 271 param.gain.left = left; 272 param.gain.right = right; 273 274 return ec_command_no_resp(component, ¶m); 275 } 276 277 static int mic_gain_put(struct snd_kcontrol *kcontrol, 278 struct snd_ctl_elem_value *ucontrol) 279 { 280 struct snd_soc_component *component = 281 snd_soc_kcontrol_component(kcontrol); 282 struct cros_ec_codec_data *codec_data = 283 snd_soc_component_get_drvdata(component); 284 int left = ucontrol->value.integer.value[0]; 285 int right = ucontrol->value.integer.value[1]; 286 unsigned int max_dmic_gain = codec_data->max_dmic_gain; 287 288 if (left > max_dmic_gain || right > max_dmic_gain) 289 return -EINVAL; 290 291 return set_ec_mic_gain(component, (u8)left, (u8)right); 292 } 293 294 static struct snd_kcontrol_new mic_gain_control = 295 SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0, 296 mic_gain_get, mic_gain_put, ec_mic_gain_tlv); 297 298 static int enable_i2s(struct snd_soc_component *component, int enable) 299 { 300 struct ec_param_codec_i2s param; 301 302 dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable); 303 304 param.cmd = EC_CODEC_I2S_ENABLE; 305 param.i2s_enable = enable; 306 307 return ec_command_no_resp(component, ¶m); 308 } 309 310 static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w, 311 struct snd_kcontrol *kcontrol, int event) 312 { 313 struct snd_soc_component *component = 314 snd_soc_dapm_to_component(w->dapm); 315 316 switch (event) { 317 case SND_SOC_DAPM_PRE_PMU: 318 dev_dbg(component->dev, 319 "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__); 320 return enable_i2s(component, 1); 321 322 case SND_SOC_DAPM_PRE_PMD: 323 dev_dbg(component->dev, 324 "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__); 325 return enable_i2s(component, 0); 326 } 327 328 return 0; 329 } 330 331 /* 332 * The goal of this DAPM route is to turn on/off I2S using EC 333 * host command when capture stream is started/stopped. 334 */ 335 static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { 336 SND_SOC_DAPM_INPUT("DMIC"), 337 338 /* 339 * Control EC to enable/disable I2S. 340 */ 341 SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM, 342 0, 0, cros_ec_i2s_enable_event, 343 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), 344 345 SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), 346 }; 347 348 static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = { 349 { "I2STX", NULL, "DMIC" }, 350 { "I2STX", NULL, "I2S Enable" }, 351 }; 352 353 /* 354 * Read maximum gain from device property and set it to mixer control. 355 */ 356 static int cros_ec_set_gain_range(struct device *dev) 357 { 358 struct soc_mixer_control *control; 359 struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev); 360 int rc; 361 362 rc = device_property_read_u32(dev, "max-dmic-gain", 363 &codec_data->max_dmic_gain); 364 if (rc) 365 return rc; 366 367 control = (struct soc_mixer_control *) 368 mic_gain_control.private_value; 369 control->max = codec_data->max_dmic_gain; 370 control->platform_max = codec_data->max_dmic_gain; 371 372 return 0; 373 } 374 375 static int cros_ec_codec_probe(struct snd_soc_component *component) 376 { 377 int rc; 378 379 struct cros_ec_codec_data *codec_data = 380 snd_soc_component_get_drvdata(component); 381 382 rc = cros_ec_set_gain_range(codec_data->dev); 383 if (rc) 384 return rc; 385 386 return snd_soc_add_component_controls(component, &mic_gain_control, 1); 387 } 388 389 static const struct snd_soc_component_driver cros_ec_component_driver = { 390 .probe = cros_ec_codec_probe, 391 .dapm_widgets = cros_ec_codec_dapm_widgets, 392 .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets), 393 .dapm_routes = cros_ec_codec_dapm_routes, 394 .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes), 395 }; 396 397 /* 398 * Platform device and platform driver fro cros-ec-codec. 399 */ 400 static int cros_ec_codec_platform_probe(struct platform_device *pd) 401 { 402 struct device *dev = &pd->dev; 403 struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent); 404 struct cros_ec_codec_data *codec_data; 405 406 codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data), 407 GFP_KERNEL); 408 if (!codec_data) 409 return -ENOMEM; 410 411 codec_data->dev = dev; 412 codec_data->ec_device = ec_device; 413 414 platform_set_drvdata(pd, codec_data); 415 416 return devm_snd_soc_register_component(dev, &cros_ec_component_driver, 417 cros_ec_dai, ARRAY_SIZE(cros_ec_dai)); 418 } 419 420 #ifdef CONFIG_OF 421 static const struct of_device_id cros_ec_codec_of_match[] = { 422 { .compatible = "google,cros-ec-codec" }, 423 {}, 424 }; 425 MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match); 426 #endif 427 428 static struct platform_driver cros_ec_codec_platform_driver = { 429 .driver = { 430 .name = DRV_NAME, 431 .of_match_table = of_match_ptr(cros_ec_codec_of_match), 432 }, 433 .probe = cros_ec_codec_platform_probe, 434 }; 435 436 module_platform_driver(cros_ec_codec_platform_driver); 437 438 MODULE_LICENSE("GPL v2"); 439 MODULE_DESCRIPTION("ChromeOS EC codec driver"); 440 MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>"); 441 MODULE_ALIAS("platform:" DRV_NAME); 442