1/* 2 * 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 * 13 * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS 14 */ 15 16#include <linux/clk.h> 17#include <linux/kernel.h> 18#include <linux/module.h> 19#include <linux/of.h> 20#include <linux/of_device.h> 21#include <linux/platform_device.h> 22#include <sound/pcm.h> 23#include <sound/pcm_params.h> 24#include <linux/regmap.h> 25#include <sound/soc.h> 26#include <sound/soc-dai.h> 27#include "lpass-lpaif-reg.h" 28#include "lpass.h" 29 30static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, 31 unsigned int freq, int dir) 32{ 33 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 34 int ret; 35 36 if (IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) 37 return 0; 38 39 ret = clk_set_rate(drvdata->mi2s_osr_clk[dai->driver->id], freq); 40 if (ret) 41 dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", 42 __func__, freq, ret); 43 44 return ret; 45} 46 47static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, 48 struct snd_soc_dai *dai) 49{ 50 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 51 int ret; 52 53 if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) { 54 ret = clk_prepare_enable( 55 drvdata->mi2s_osr_clk[dai->driver->id]); 56 if (ret) { 57 dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", 58 __func__, ret); 59 return ret; 60 } 61 } 62 63 ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]); 64 if (ret) { 65 dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", 66 __func__, ret); 67 if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) 68 clk_disable_unprepare( 69 drvdata->mi2s_osr_clk[dai->driver->id]); 70 return ret; 71 } 72 73 return 0; 74} 75 76static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, 77 struct snd_soc_dai *dai) 78{ 79 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 80 81 clk_disable_unprepare(drvdata->mi2s_bit_clk[dai->driver->id]); 82 83 if (!IS_ERR(drvdata->mi2s_osr_clk[dai->driver->id])) 84 clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); 85} 86 87static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, 88 struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) 89{ 90 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 91 snd_pcm_format_t format = params_format(params); 92 unsigned int channels = params_channels(params); 93 unsigned int rate = params_rate(params); 94 unsigned int regval; 95 int bitwidth, ret; 96 97 bitwidth = snd_pcm_format_width(format); 98 if (bitwidth < 0) { 99 dev_err(dai->dev, "%s() invalid bit width given: %d\n", 100 __func__, bitwidth); 101 return bitwidth; 102 } 103 104 regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | 105 LPAIF_I2SCTL_WSSRC_INTERNAL; 106 107 switch (bitwidth) { 108 case 16: 109 regval |= LPAIF_I2SCTL_BITWIDTH_16; 110 break; 111 case 24: 112 regval |= LPAIF_I2SCTL_BITWIDTH_24; 113 break; 114 case 32: 115 regval |= LPAIF_I2SCTL_BITWIDTH_32; 116 break; 117 default: 118 dev_err(dai->dev, "%s() invalid bitwidth given: %d\n", 119 __func__, bitwidth); 120 return -EINVAL; 121 } 122 123 switch (channels) { 124 case 1: 125 regval |= LPAIF_I2SCTL_SPKMODE_SD0; 126 regval |= LPAIF_I2SCTL_SPKMONO_MONO; 127 break; 128 case 2: 129 regval |= LPAIF_I2SCTL_SPKMODE_SD0; 130 regval |= LPAIF_I2SCTL_SPKMONO_STEREO; 131 break; 132 case 4: 133 regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; 134 regval |= LPAIF_I2SCTL_SPKMONO_STEREO; 135 break; 136 case 6: 137 regval |= LPAIF_I2SCTL_SPKMODE_6CH; 138 regval |= LPAIF_I2SCTL_SPKMONO_STEREO; 139 break; 140 case 8: 141 regval |= LPAIF_I2SCTL_SPKMODE_8CH; 142 regval |= LPAIF_I2SCTL_SPKMONO_STEREO; 143 break; 144 default: 145 dev_err(dai->dev, "%s() invalid channels given: %u\n", 146 __func__, channels); 147 return -EINVAL; 148 } 149 150 ret = regmap_write(drvdata->lpaif_map, 151 LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 152 regval); 153 if (ret) { 154 dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", 155 __func__, ret); 156 return ret; 157 } 158 159 ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id], 160 rate * bitwidth * 2); 161 if (ret) { 162 dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", 163 __func__, rate * bitwidth * 2, ret); 164 return ret; 165 } 166 167 return 0; 168} 169 170static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, 171 struct snd_soc_dai *dai) 172{ 173 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 174 int ret; 175 176 ret = regmap_write(drvdata->lpaif_map, 177 LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 178 0); 179 if (ret) 180 dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", 181 __func__, ret); 182 183 return ret; 184} 185 186static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, 187 struct snd_soc_dai *dai) 188{ 189 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 190 int ret; 191 192 ret = regmap_update_bits(drvdata->lpaif_map, 193 LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 194 LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); 195 if (ret) 196 dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", 197 __func__, ret); 198 199 return ret; 200} 201 202static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, 203 int cmd, struct snd_soc_dai *dai) 204{ 205 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 206 int ret = -EINVAL; 207 208 switch (cmd) { 209 case SNDRV_PCM_TRIGGER_START: 210 case SNDRV_PCM_TRIGGER_RESUME: 211 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 212 ret = regmap_update_bits(drvdata->lpaif_map, 213 LPAIF_I2SCTL_REG(drvdata->variant, 214 dai->driver->id), 215 LPAIF_I2SCTL_SPKEN_MASK, 216 LPAIF_I2SCTL_SPKEN_ENABLE); 217 if (ret) 218 dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", 219 __func__, ret); 220 break; 221 case SNDRV_PCM_TRIGGER_STOP: 222 case SNDRV_PCM_TRIGGER_SUSPEND: 223 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 224 ret = regmap_update_bits(drvdata->lpaif_map, 225 LPAIF_I2SCTL_REG(drvdata->variant, 226 dai->driver->id), 227 LPAIF_I2SCTL_SPKEN_MASK, 228 LPAIF_I2SCTL_SPKEN_DISABLE); 229 if (ret) 230 dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", 231 __func__, ret); 232 break; 233 } 234 235 return ret; 236} 237 238const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { 239 .set_sysclk = lpass_cpu_daiops_set_sysclk, 240 .startup = lpass_cpu_daiops_startup, 241 .shutdown = lpass_cpu_daiops_shutdown, 242 .hw_params = lpass_cpu_daiops_hw_params, 243 .hw_free = lpass_cpu_daiops_hw_free, 244 .prepare = lpass_cpu_daiops_prepare, 245 .trigger = lpass_cpu_daiops_trigger, 246}; 247EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_ops); 248 249int asoc_qcom_lpass_cpu_dai_probe(struct snd_soc_dai *dai) 250{ 251 struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); 252 int ret; 253 254 /* ensure audio hardware is disabled */ 255 ret = regmap_write(drvdata->lpaif_map, 256 LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), 0); 257 if (ret) 258 dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", 259 __func__, ret); 260 261 return ret; 262} 263EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_dai_probe); 264 265static const struct snd_soc_component_driver lpass_cpu_comp_driver = { 266 .name = "lpass-cpu", 267}; 268 269static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) 270{ 271 struct lpass_data *drvdata = dev_get_drvdata(dev); 272 struct lpass_variant *v = drvdata->variant; 273 int i; 274 275 for (i = 0; i < v->i2s_ports; ++i) 276 if (reg == LPAIF_I2SCTL_REG(v, i)) 277 return true; 278 279 for (i = 0; i < v->irq_ports; ++i) { 280 if (reg == LPAIF_IRQEN_REG(v, i)) 281 return true; 282 if (reg == LPAIF_IRQCLEAR_REG(v, i)) 283 return true; 284 } 285 286 for (i = 0; i < v->rdma_channels; ++i) { 287 if (reg == LPAIF_RDMACTL_REG(v, i)) 288 return true; 289 if (reg == LPAIF_RDMABASE_REG(v, i)) 290 return true; 291 if (reg == LPAIF_RDMABUFF_REG(v, i)) 292 return true; 293 if (reg == LPAIF_RDMAPER_REG(v, i)) 294 return true; 295 } 296 297 return false; 298} 299 300static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) 301{ 302 struct lpass_data *drvdata = dev_get_drvdata(dev); 303 struct lpass_variant *v = drvdata->variant; 304 int i; 305 306 for (i = 0; i < v->i2s_ports; ++i) 307 if (reg == LPAIF_I2SCTL_REG(v, i)) 308 return true; 309 310 for (i = 0; i < v->irq_ports; ++i) { 311 if (reg == LPAIF_IRQEN_REG(v, i)) 312 return true; 313 if (reg == LPAIF_IRQSTAT_REG(v, i)) 314 return true; 315 } 316 317 for (i = 0; i < v->rdma_channels; ++i) { 318 if (reg == LPAIF_RDMACTL_REG(v, i)) 319 return true; 320 if (reg == LPAIF_RDMABASE_REG(v, i)) 321 return true; 322 if (reg == LPAIF_RDMABUFF_REG(v, i)) 323 return true; 324 if (reg == LPAIF_RDMACURR_REG(v, i)) 325 return true; 326 if (reg == LPAIF_RDMAPER_REG(v, i)) 327 return true; 328 } 329 330 return false; 331} 332 333static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) 334{ 335 struct lpass_data *drvdata = dev_get_drvdata(dev); 336 struct lpass_variant *v = drvdata->variant; 337 int i; 338 339 for (i = 0; i < v->irq_ports; ++i) 340 if (reg == LPAIF_IRQSTAT_REG(v, i)) 341 return true; 342 343 for (i = 0; i < v->rdma_channels; ++i) 344 if (reg == LPAIF_RDMACURR_REG(v, i)) 345 return true; 346 347 return false; 348} 349 350static struct regmap_config lpass_cpu_regmap_config = { 351 .reg_bits = 32, 352 .reg_stride = 4, 353 .val_bits = 32, 354 .writeable_reg = lpass_cpu_regmap_writeable, 355 .readable_reg = lpass_cpu_regmap_readable, 356 .volatile_reg = lpass_cpu_regmap_volatile, 357 .cache_type = REGCACHE_FLAT, 358}; 359 360int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) 361{ 362 struct lpass_data *drvdata; 363 struct device_node *dsp_of_node; 364 struct resource *res; 365 struct lpass_variant *variant; 366 struct device *dev = &pdev->dev; 367 const struct of_device_id *match; 368 char clk_name[16]; 369 int ret, i, dai_id; 370 371 dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); 372 if (dsp_of_node) { 373 dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n", 374 __func__); 375 return -EBUSY; 376 } 377 378 drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data), 379 GFP_KERNEL); 380 if (!drvdata) 381 return -ENOMEM; 382 platform_set_drvdata(pdev, drvdata); 383 384 match = of_match_device(dev->driver->of_match_table, dev); 385 if (!match || !match->data) 386 return -EINVAL; 387 388 drvdata->variant = (struct lpass_variant *)match->data; 389 variant = drvdata->variant; 390 391 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); 392 393 drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); 394 if (IS_ERR((void const __force *)drvdata->lpaif)) { 395 dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n", 396 __func__, 397 PTR_ERR((void const __force *)drvdata->lpaif)); 398 return PTR_ERR((void const __force *)drvdata->lpaif); 399 } 400 401 lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant, 402 variant->rdma_channels); 403 404 drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, 405 &lpass_cpu_regmap_config); 406 if (IS_ERR(drvdata->lpaif_map)) { 407 dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n", 408 __func__, PTR_ERR(drvdata->lpaif_map)); 409 return PTR_ERR(drvdata->lpaif_map); 410 } 411 412 if (variant->init) 413 variant->init(pdev); 414 415 for (i = 0; i < variant->num_dai; i++) { 416 dai_id = variant->dai_driver[i].id; 417 if (variant->num_dai > 1) 418 sprintf(clk_name, "mi2s-osr-clk%d", i); 419 else 420 sprintf(clk_name, "mi2s-osr-clk"); 421 422 drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev, 423 clk_name); 424 if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { 425 dev_warn(&pdev->dev, 426 "%s() error getting mi2s-osr-clk: %ld\n", 427 __func__, 428 PTR_ERR(drvdata->mi2s_osr_clk[dai_id])); 429 } 430 431 if (variant->num_dai > 1) 432 sprintf(clk_name, "mi2s-bit-clk%d", i); 433 else 434 sprintf(clk_name, "mi2s-bit-clk"); 435 436 drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev, 437 clk_name); 438 if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) { 439 dev_err(&pdev->dev, 440 "%s() error getting mi2s-bit-clk: %ld\n", 441 __func__, 442 PTR_ERR(drvdata->mi2s_bit_clk[dai_id])); 443 return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]); 444 } 445 } 446 447 drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); 448 if (IS_ERR(drvdata->ahbix_clk)) { 449 dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n", 450 __func__, PTR_ERR(drvdata->ahbix_clk)); 451 return PTR_ERR(drvdata->ahbix_clk); 452 } 453 454 ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); 455 if (ret) { 456 dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n", 457 __func__, ret); 458 return ret; 459 } 460 dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__, 461 clk_get_rate(drvdata->ahbix_clk)); 462 463 ret = clk_prepare_enable(drvdata->ahbix_clk); 464 if (ret) { 465 dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n", 466 __func__, ret); 467 return ret; 468 } 469 470 ret = devm_snd_soc_register_component(&pdev->dev, 471 &lpass_cpu_comp_driver, 472 variant->dai_driver, 473 variant->num_dai); 474 if (ret) { 475 dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", 476 __func__, ret); 477 goto err_clk; 478 } 479 480 ret = asoc_qcom_lpass_platform_register(pdev); 481 if (ret) { 482 dev_err(&pdev->dev, "%s() error registering platform driver: %d\n", 483 __func__, ret); 484 goto err_clk; 485 } 486 487 return 0; 488 489err_clk: 490 clk_disable_unprepare(drvdata->ahbix_clk); 491 return ret; 492} 493EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); 494 495int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) 496{ 497 struct lpass_data *drvdata = platform_get_drvdata(pdev); 498 499 if (drvdata->variant->exit) 500 drvdata->variant->exit(pdev); 501 502 clk_disable_unprepare(drvdata->ahbix_clk); 503 504 return 0; 505} 506EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_remove); 507