1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * McBSP Sidetone support 4 * 5 * Copyright (C) 2004 Nokia Corporation 6 * Author: Samuel Ortiz <samuel.ortiz@nokia.com> 7 * 8 * Contact: Jarkko Nikula <jarkko.nikula@bitmer.com> 9 * Peter Ujfalusi <peter.ujfalusi@ti.com> 10 */ 11 12 #include <linux/module.h> 13 #include <linux/init.h> 14 #include <linux/device.h> 15 #include <linux/platform_device.h> 16 #include <linux/interrupt.h> 17 #include <linux/err.h> 18 #include <linux/clk.h> 19 #include <linux/delay.h> 20 #include <linux/io.h> 21 #include <linux/slab.h> 22 #include <linux/pm_runtime.h> 23 24 #include "omap-mcbsp.h" 25 #include "omap-mcbsp-priv.h" 26 27 /* OMAP3 sidetone control registers */ 28 #define OMAP_ST_REG_REV 0x00 29 #define OMAP_ST_REG_SYSCONFIG 0x10 30 #define OMAP_ST_REG_IRQSTATUS 0x18 31 #define OMAP_ST_REG_IRQENABLE 0x1C 32 #define OMAP_ST_REG_SGAINCR 0x24 33 #define OMAP_ST_REG_SFIRCR 0x28 34 #define OMAP_ST_REG_SSELCR 0x2C 35 36 /********************** McBSP SSELCR bit definitions ***********************/ 37 #define SIDETONEEN BIT(10) 38 39 /********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ 40 #define ST_AUTOIDLE BIT(0) 41 42 /********************** McBSP Sidetone SGAINCR bit definitions *************/ 43 #define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ 44 #define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ 45 46 /********************** McBSP Sidetone SFIRCR bit definitions **************/ 47 #define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ 48 49 /********************** McBSP Sidetone SSELCR bit definitions **************/ 50 #define ST_SIDETONEEN BIT(0) 51 #define ST_COEFFWREN BIT(1) 52 #define ST_COEFFWRDONE BIT(2) 53 54 struct omap_mcbsp_st_data { 55 void __iomem *io_base_st; 56 struct clk *mcbsp_iclk; 57 bool running; 58 bool enabled; 59 s16 taps[128]; /* Sidetone filter coefficients */ 60 int nr_taps; /* Number of filter coefficients in use */ 61 s16 ch0gain; 62 s16 ch1gain; 63 }; 64 65 static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) 66 { 67 writel_relaxed(val, mcbsp->st_data->io_base_st + reg); 68 } 69 70 static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) 71 { 72 return readl_relaxed(mcbsp->st_data->io_base_st + reg); 73 } 74 75 #define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) 76 #define MCBSP_ST_WRITE(mcbsp, reg, val) \ 77 omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) 78 79 static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) 80 { 81 unsigned int w; 82 83 if (mcbsp->pdata->force_ick_on) 84 mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); 85 86 /* Disable Sidetone clock auto-gating for normal operation */ 87 w = MCBSP_ST_READ(mcbsp, SYSCONFIG); 88 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); 89 90 /* Enable McBSP Sidetone */ 91 w = MCBSP_READ(mcbsp, SSELCR); 92 MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); 93 94 /* Enable Sidetone from Sidetone Core */ 95 w = MCBSP_ST_READ(mcbsp, SSELCR); 96 MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); 97 } 98 99 static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) 100 { 101 unsigned int w; 102 103 w = MCBSP_ST_READ(mcbsp, SSELCR); 104 MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); 105 106 w = MCBSP_READ(mcbsp, SSELCR); 107 MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); 108 109 /* Enable Sidetone clock auto-gating to reduce power consumption */ 110 w = MCBSP_ST_READ(mcbsp, SYSCONFIG); 111 MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); 112 113 if (mcbsp->pdata->force_ick_on) 114 mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); 115 } 116 117 static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) 118 { 119 u16 val, i; 120 121 val = MCBSP_ST_READ(mcbsp, SSELCR); 122 123 if (val & ST_COEFFWREN) 124 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); 125 126 MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); 127 128 for (i = 0; i < 128; i++) 129 MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); 130 131 i = 0; 132 133 val = MCBSP_ST_READ(mcbsp, SSELCR); 134 while (!(val & ST_COEFFWRDONE) && (++i < 1000)) 135 val = MCBSP_ST_READ(mcbsp, SSELCR); 136 137 MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); 138 139 if (i == 1000) 140 dev_err(mcbsp->dev, "McBSP FIR load error!\n"); 141 } 142 143 static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) 144 { 145 u16 w; 146 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 147 148 w = MCBSP_ST_READ(mcbsp, SSELCR); 149 150 MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | 151 ST_CH1GAIN(st_data->ch1gain)); 152 } 153 154 static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, 155 s16 chgain) 156 { 157 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 158 int ret = 0; 159 160 if (!st_data) 161 return -ENOENT; 162 163 spin_lock_irq(&mcbsp->lock); 164 if (channel == 0) 165 st_data->ch0gain = chgain; 166 else if (channel == 1) 167 st_data->ch1gain = chgain; 168 else 169 ret = -EINVAL; 170 171 if (st_data->enabled) 172 omap_mcbsp_st_chgain(mcbsp); 173 spin_unlock_irq(&mcbsp->lock); 174 175 return ret; 176 } 177 178 static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, 179 s16 *chgain) 180 { 181 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 182 int ret = 0; 183 184 if (!st_data) 185 return -ENOENT; 186 187 spin_lock_irq(&mcbsp->lock); 188 if (channel == 0) 189 *chgain = st_data->ch0gain; 190 else if (channel == 1) 191 *chgain = st_data->ch1gain; 192 else 193 ret = -EINVAL; 194 spin_unlock_irq(&mcbsp->lock); 195 196 return ret; 197 } 198 199 static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) 200 { 201 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 202 203 if (!st_data) 204 return -ENODEV; 205 206 spin_lock_irq(&mcbsp->lock); 207 st_data->enabled = 1; 208 omap_mcbsp_st_start(mcbsp); 209 spin_unlock_irq(&mcbsp->lock); 210 211 return 0; 212 } 213 214 static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) 215 { 216 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 217 int ret = 0; 218 219 if (!st_data) 220 return -ENODEV; 221 222 spin_lock_irq(&mcbsp->lock); 223 omap_mcbsp_st_stop(mcbsp); 224 st_data->enabled = 0; 225 spin_unlock_irq(&mcbsp->lock); 226 227 return ret; 228 } 229 230 static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) 231 { 232 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 233 234 if (!st_data) 235 return -ENODEV; 236 237 return st_data->enabled; 238 } 239 240 static ssize_t st_taps_show(struct device *dev, 241 struct device_attribute *attr, char *buf) 242 { 243 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); 244 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 245 ssize_t status = 0; 246 int i; 247 248 spin_lock_irq(&mcbsp->lock); 249 for (i = 0; i < st_data->nr_taps; i++) 250 status += sprintf(&buf[status], (i ? ", %d" : "%d"), 251 st_data->taps[i]); 252 if (i) 253 status += sprintf(&buf[status], "\n"); 254 spin_unlock_irq(&mcbsp->lock); 255 256 return status; 257 } 258 259 static ssize_t st_taps_store(struct device *dev, 260 struct device_attribute *attr, 261 const char *buf, size_t size) 262 { 263 struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); 264 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 265 int val, tmp, status, i = 0; 266 267 spin_lock_irq(&mcbsp->lock); 268 memset(st_data->taps, 0, sizeof(st_data->taps)); 269 st_data->nr_taps = 0; 270 271 do { 272 status = sscanf(buf, "%d%n", &val, &tmp); 273 if (status < 0 || status == 0) { 274 size = -EINVAL; 275 goto out; 276 } 277 if (val < -32768 || val > 32767) { 278 size = -EINVAL; 279 goto out; 280 } 281 st_data->taps[i++] = val; 282 buf += tmp; 283 if (*buf != ',') 284 break; 285 buf++; 286 } while (1); 287 288 st_data->nr_taps = i; 289 290 out: 291 spin_unlock_irq(&mcbsp->lock); 292 293 return size; 294 } 295 296 static DEVICE_ATTR_RW(st_taps); 297 298 static const struct attribute *sidetone_attrs[] = { 299 &dev_attr_st_taps.attr, 300 NULL, 301 }; 302 303 static const struct attribute_group sidetone_attr_group = { 304 .attrs = (struct attribute **)sidetone_attrs, 305 }; 306 307 int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) 308 { 309 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 310 311 if (st_data->enabled && !st_data->running) { 312 omap_mcbsp_st_fir_write(mcbsp, st_data->taps); 313 omap_mcbsp_st_chgain(mcbsp); 314 315 if (!mcbsp->free) { 316 omap_mcbsp_st_on(mcbsp); 317 st_data->running = 1; 318 } 319 } 320 321 return 0; 322 } 323 324 int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) 325 { 326 struct omap_mcbsp_st_data *st_data = mcbsp->st_data; 327 328 if (st_data->running) { 329 if (!mcbsp->free) { 330 omap_mcbsp_st_off(mcbsp); 331 st_data->running = 0; 332 } 333 } 334 335 return 0; 336 } 337 338 int omap_mcbsp_st_init(struct platform_device *pdev) 339 { 340 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); 341 struct omap_mcbsp_st_data *st_data; 342 struct resource *res; 343 int ret; 344 345 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); 346 if (!res) 347 return 0; 348 349 st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); 350 if (!st_data) 351 return -ENOMEM; 352 353 st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); 354 if (IS_ERR(st_data->mcbsp_iclk)) { 355 dev_warn(mcbsp->dev, 356 "Failed to get ick, sidetone might be broken\n"); 357 st_data->mcbsp_iclk = NULL; 358 } 359 360 st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, 361 resource_size(res)); 362 if (!st_data->io_base_st) 363 return -ENOMEM; 364 365 ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); 366 if (ret) 367 return ret; 368 369 mcbsp->st_data = st_data; 370 371 return 0; 372 } 373 374 void omap_mcbsp_st_cleanup(struct platform_device *pdev) 375 { 376 struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); 377 378 if (mcbsp->st_data) { 379 sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); 380 clk_put(mcbsp->st_data->mcbsp_iclk); 381 } 382 } 383 384 static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, 385 struct snd_ctl_elem_info *uinfo) 386 { 387 struct soc_mixer_control *mc = 388 (struct soc_mixer_control *)kcontrol->private_value; 389 int max = mc->max; 390 int min = mc->min; 391 392 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; 393 uinfo->count = 1; 394 uinfo->value.integer.min = min; 395 uinfo->value.integer.max = max; 396 return 0; 397 } 398 399 #define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ 400 static int \ 401 omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ 402 struct snd_ctl_elem_value *uc) \ 403 { \ 404 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ 405 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ 406 struct soc_mixer_control *mc = \ 407 (struct soc_mixer_control *)kc->private_value; \ 408 int max = mc->max; \ 409 int min = mc->min; \ 410 int val = uc->value.integer.value[0]; \ 411 \ 412 if (val < min || val > max) \ 413 return -EINVAL; \ 414 \ 415 /* OMAP McBSP implementation uses index values 0..4 */ \ 416 return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ 417 } \ 418 \ 419 static int \ 420 omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ 421 struct snd_ctl_elem_value *uc) \ 422 { \ 423 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ 424 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ 425 s16 chgain; \ 426 \ 427 if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ 428 return -EAGAIN; \ 429 \ 430 uc->value.integer.value[0] = chgain; \ 431 return 0; \ 432 } 433 434 OMAP_MCBSP_ST_CHANNEL_VOLUME(0) 435 OMAP_MCBSP_ST_CHANNEL_VOLUME(1) 436 437 static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, 438 struct snd_ctl_elem_value *ucontrol) 439 { 440 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 441 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 442 u8 value = ucontrol->value.integer.value[0]; 443 444 if (value == omap_mcbsp_st_is_enabled(mcbsp)) 445 return 0; 446 447 if (value) 448 omap_mcbsp_st_enable(mcbsp); 449 else 450 omap_mcbsp_st_disable(mcbsp); 451 452 return 1; 453 } 454 455 static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, 456 struct snd_ctl_elem_value *ucontrol) 457 { 458 struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); 459 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 460 461 ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); 462 return 0; 463 } 464 465 #define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ 466 xhandler_get, xhandler_put) \ 467 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ 468 .info = omap_mcbsp_st_info_volsw, \ 469 .get = xhandler_get, .put = xhandler_put, \ 470 .private_value = (unsigned long)&(struct soc_mixer_control) \ 471 {.min = xmin, .max = xmax} } 472 473 #define OMAP_MCBSP_ST_CONTROLS(port) \ 474 static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ 475 SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ 476 omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ 477 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ 478 -32768, 32767, \ 479 omap_mcbsp_get_st_ch0_volume, \ 480 omap_mcbsp_set_st_ch0_volume), \ 481 OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ 482 -32768, 32767, \ 483 omap_mcbsp_get_st_ch1_volume, \ 484 omap_mcbsp_set_st_ch1_volume), \ 485 } 486 487 OMAP_MCBSP_ST_CONTROLS(2); 488 OMAP_MCBSP_ST_CONTROLS(3); 489 490 int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) 491 { 492 struct snd_soc_dai *cpu_dai = rtd->cpu_dai; 493 struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); 494 495 if (!mcbsp->st_data) { 496 dev_warn(mcbsp->dev, "No sidetone data for port\n"); 497 return 0; 498 } 499 500 switch (port_id) { 501 case 2: /* McBSP 2 */ 502 return snd_soc_add_dai_controls(cpu_dai, 503 omap_mcbsp2_st_controls, 504 ARRAY_SIZE(omap_mcbsp2_st_controls)); 505 case 3: /* McBSP 3 */ 506 return snd_soc_add_dai_controls(cpu_dai, 507 omap_mcbsp3_st_controls, 508 ARRAY_SIZE(omap_mcbsp3_st_controls)); 509 default: 510 dev_err(mcbsp->dev, "Port %d not supported\n", port_id); 511 break; 512 } 513 514 return -EINVAL; 515 } 516 EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); 517