1/* 2 * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library 3 * 4 * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com 5 * 6 * Author: Jyri Sarha <jsarha@ti.com> 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * version 2 as published by the Free Software Foundation. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 */ 18 19#include <linux/kernel.h> 20#include <linux/module.h> 21#include <linux/err.h> 22#include <linux/string.h> 23#include <linux/platform_device.h> 24#include <sound/soc.h> 25#include <sound/pcm_params.h> 26#include <sound/dmaengine_pcm.h> 27#include <uapi/sound/asound.h> 28#include <sound/asoundef.h> 29#include <sound/omap-pcm.h> 30#include <sound/omap-hdmi-audio.h> 31#include <video/omapdss.h> 32 33#define DRV_NAME "omap-hdmi-audio" 34 35struct hdmi_audio_data { 36 struct snd_soc_card *card; 37 38 const struct omap_hdmi_audio_ops *ops; 39 struct device *dssdev; 40 struct snd_dmaengine_dai_dma_data dma_data; 41 struct omap_dss_audio dss_audio; 42 struct snd_aes_iec958 iec; 43 struct snd_cea_861_aud_if cea; 44 45 struct mutex current_stream_lock; 46 struct snd_pcm_substream *current_stream; 47}; 48 49static 50struct hdmi_audio_data *card_drvdata_substream(struct snd_pcm_substream *ss) 51{ 52 struct snd_soc_pcm_runtime *rtd = ss->private_data; 53 54 return snd_soc_card_get_drvdata(rtd->card); 55} 56 57static void hdmi_dai_abort(struct device *dev) 58{ 59 struct hdmi_audio_data *ad = dev_get_drvdata(dev); 60 61 mutex_lock(&ad->current_stream_lock); 62 if (ad->current_stream && ad->current_stream->runtime && 63 snd_pcm_running(ad->current_stream)) { 64 dev_err(dev, "HDMI display disabled, aborting playback\n"); 65 snd_pcm_stream_lock_irq(ad->current_stream); 66 snd_pcm_stop(ad->current_stream, SNDRV_PCM_STATE_DISCONNECTED); 67 snd_pcm_stream_unlock_irq(ad->current_stream); 68 } 69 mutex_unlock(&ad->current_stream_lock); 70} 71 72static int hdmi_dai_startup(struct snd_pcm_substream *substream, 73 struct snd_soc_dai *dai) 74{ 75 struct hdmi_audio_data *ad = card_drvdata_substream(substream); 76 int ret; 77 /* 78 * Make sure that the period bytes are multiple of the DMA packet size. 79 * Largest packet size we use is 32 32-bit words = 128 bytes 80 */ 81 ret = snd_pcm_hw_constraint_step(substream->runtime, 0, 82 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); 83 if (ret < 0) { 84 dev_err(dai->dev, "Could not apply period constraint: %d\n", 85 ret); 86 return ret; 87 } 88 ret = snd_pcm_hw_constraint_step(substream->runtime, 0, 89 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128); 90 if (ret < 0) { 91 dev_err(dai->dev, "Could not apply buffer constraint: %d\n", 92 ret); 93 return ret; 94 } 95 96 snd_soc_dai_set_dma_data(dai, substream, &ad->dma_data); 97 98 mutex_lock(&ad->current_stream_lock); 99 ad->current_stream = substream; 100 mutex_unlock(&ad->current_stream_lock); 101 102 ret = ad->ops->audio_startup(ad->dssdev, hdmi_dai_abort); 103 104 if (ret) { 105 mutex_lock(&ad->current_stream_lock); 106 ad->current_stream = NULL; 107 mutex_unlock(&ad->current_stream_lock); 108 } 109 110 return ret; 111} 112 113static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, 114 struct snd_pcm_hw_params *params, 115 struct snd_soc_dai *dai) 116{ 117 struct hdmi_audio_data *ad = card_drvdata_substream(substream); 118 struct snd_aes_iec958 *iec = &ad->iec; 119 struct snd_cea_861_aud_if *cea = &ad->cea; 120 121 WARN_ON(ad->current_stream != substream); 122 123 switch (params_format(params)) { 124 case SNDRV_PCM_FORMAT_S16_LE: 125 ad->dma_data.maxburst = 16; 126 break; 127 case SNDRV_PCM_FORMAT_S24_LE: 128 ad->dma_data.maxburst = 32; 129 break; 130 default: 131 dev_err(dai->dev, "format not supported!\n"); 132 return -EINVAL; 133 } 134 135 ad->dss_audio.iec = iec; 136 ad->dss_audio.cea = cea; 137 /* 138 * fill the IEC-60958 channel status word 139 */ 140 /* initialize the word bytes */ 141 memset(iec->status, 0, sizeof(iec->status)); 142 143 /* specify IEC-60958-3 (commercial use) */ 144 iec->status[0] &= ~IEC958_AES0_PROFESSIONAL; 145 146 /* specify that the audio is LPCM*/ 147 iec->status[0] &= ~IEC958_AES0_NONAUDIO; 148 149 iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; 150 151 iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; 152 153 iec->status[1] = IEC958_AES1_CON_GENERAL; 154 155 iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; 156 157 iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC; 158 159 switch (params_rate(params)) { 160 case 32000: 161 iec->status[3] |= IEC958_AES3_CON_FS_32000; 162 break; 163 case 44100: 164 iec->status[3] |= IEC958_AES3_CON_FS_44100; 165 break; 166 case 48000: 167 iec->status[3] |= IEC958_AES3_CON_FS_48000; 168 break; 169 case 88200: 170 iec->status[3] |= IEC958_AES3_CON_FS_88200; 171 break; 172 case 96000: 173 iec->status[3] |= IEC958_AES3_CON_FS_96000; 174 break; 175 case 176400: 176 iec->status[3] |= IEC958_AES3_CON_FS_176400; 177 break; 178 case 192000: 179 iec->status[3] |= IEC958_AES3_CON_FS_192000; 180 break; 181 default: 182 dev_err(dai->dev, "rate not supported!\n"); 183 return -EINVAL; 184 } 185 186 /* specify the clock accuracy */ 187 iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM; 188 189 /* 190 * specify the word length. The same word length value can mean 191 * two different lengths. Hence, we need to specify the maximum 192 * word length as well. 193 */ 194 switch (params_format(params)) { 195 case SNDRV_PCM_FORMAT_S16_LE: 196 iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16; 197 iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24; 198 break; 199 case SNDRV_PCM_FORMAT_S24_LE: 200 iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20; 201 iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24; 202 break; 203 default: 204 dev_err(dai->dev, "format not supported!\n"); 205 return -EINVAL; 206 } 207 208 /* 209 * Fill the CEA-861 audio infoframe (see spec for details) 210 */ 211 212 cea->db1_ct_cc = (params_channels(params) - 1) 213 & CEA861_AUDIO_INFOFRAME_DB1CC; 214 cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM; 215 216 cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM; 217 cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM; 218 219 cea->db3 = 0; /* not used, all zeros */ 220 221 if (params_channels(params) == 2) 222 cea->db4_ca = 0x0; 223 else if (params_channels(params) == 6) 224 cea->db4_ca = 0xb; 225 else 226 cea->db4_ca = 0x13; 227 228 if (cea->db4_ca == 0x00) 229 cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED; 230 else 231 cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED; 232 233 /* the expression is trivial but makes clear what we are doing */ 234 cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV); 235 236 return ad->ops->audio_config(ad->dssdev, &ad->dss_audio); 237} 238 239static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd, 240 struct snd_soc_dai *dai) 241{ 242 struct hdmi_audio_data *ad = card_drvdata_substream(substream); 243 int err = 0; 244 245 WARN_ON(ad->current_stream != substream); 246 247 switch (cmd) { 248 case SNDRV_PCM_TRIGGER_START: 249 case SNDRV_PCM_TRIGGER_RESUME: 250 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 251 err = ad->ops->audio_start(ad->dssdev); 252 break; 253 case SNDRV_PCM_TRIGGER_STOP: 254 case SNDRV_PCM_TRIGGER_SUSPEND: 255 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 256 ad->ops->audio_stop(ad->dssdev); 257 break; 258 default: 259 err = -EINVAL; 260 } 261 return err; 262} 263 264static void hdmi_dai_shutdown(struct snd_pcm_substream *substream, 265 struct snd_soc_dai *dai) 266{ 267 struct hdmi_audio_data *ad = card_drvdata_substream(substream); 268 269 WARN_ON(ad->current_stream != substream); 270 271 ad->ops->audio_shutdown(ad->dssdev); 272 273 mutex_lock(&ad->current_stream_lock); 274 ad->current_stream = NULL; 275 mutex_unlock(&ad->current_stream_lock); 276} 277 278static const struct snd_soc_dai_ops hdmi_dai_ops = { 279 .startup = hdmi_dai_startup, 280 .hw_params = hdmi_dai_hw_params, 281 .trigger = hdmi_dai_trigger, 282 .shutdown = hdmi_dai_shutdown, 283}; 284 285static const struct snd_soc_component_driver omap_hdmi_component = { 286 .name = "omapdss_hdmi", 287}; 288 289static struct snd_soc_dai_driver omap5_hdmi_dai = { 290 .name = "omap5-hdmi-dai", 291 .playback = { 292 .channels_min = 2, 293 .channels_max = 8, 294 .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 295 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | 296 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | 297 SNDRV_PCM_RATE_192000), 298 .formats = SNDRV_PCM_FMTBIT_S16_LE, 299 }, 300 .ops = &hdmi_dai_ops, 301}; 302 303static struct snd_soc_dai_driver omap4_hdmi_dai = { 304 .name = "omap4-hdmi-dai", 305 .playback = { 306 .channels_min = 2, 307 .channels_max = 8, 308 .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | 309 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | 310 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | 311 SNDRV_PCM_RATE_192000), 312 .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, 313 }, 314 .ops = &hdmi_dai_ops, 315}; 316 317static int omap_hdmi_audio_probe(struct platform_device *pdev) 318{ 319 struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data; 320 struct device *dev = &pdev->dev; 321 struct hdmi_audio_data *ad; 322 struct snd_soc_dai_driver *dai_drv; 323 struct snd_soc_card *card; 324 int ret; 325 326 if (!ha) { 327 dev_err(dev, "No platform data\n"); 328 return -EINVAL; 329 } 330 331 ad = devm_kzalloc(dev, sizeof(*ad), GFP_KERNEL); 332 if (!ad) 333 return -ENOMEM; 334 ad->dssdev = ha->dev; 335 ad->ops = ha->ops; 336 ad->dma_data.addr = ha->audio_dma_addr; 337 ad->dma_data.filter_data = "audio_tx"; 338 ad->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 339 mutex_init(&ad->current_stream_lock); 340 341 switch (ha->dss_version) { 342 case OMAPDSS_VER_OMAP4430_ES1: 343 case OMAPDSS_VER_OMAP4430_ES2: 344 case OMAPDSS_VER_OMAP4: 345 dai_drv = &omap4_hdmi_dai; 346 break; 347 case OMAPDSS_VER_OMAP5: 348 dai_drv = &omap5_hdmi_dai; 349 break; 350 default: 351 return -EINVAL; 352 } 353 ret = snd_soc_register_component(ad->dssdev, &omap_hdmi_component, 354 dai_drv, 1); 355 if (ret) 356 return ret; 357 358 ret = omap_pcm_platform_register(ad->dssdev); 359 if (ret) 360 return ret; 361 362 card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 363 if (!card) 364 return -ENOMEM; 365 366 card->name = devm_kasprintf(dev, GFP_KERNEL, 367 "HDMI %s", dev_name(ad->dssdev)); 368 card->owner = THIS_MODULE; 369 card->dai_link = 370 devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); 371 card->dai_link->name = card->name; 372 card->dai_link->stream_name = card->name; 373 card->dai_link->cpu_dai_name = dev_name(ad->dssdev); 374 card->dai_link->platform_name = dev_name(ad->dssdev); 375 card->dai_link->codec_name = "snd-soc-dummy"; 376 card->dai_link->codec_dai_name = "snd-soc-dummy-dai"; 377 card->num_links = 1; 378 card->dev = dev; 379 380 ret = snd_soc_register_card(card); 381 if (ret) { 382 dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); 383 snd_soc_unregister_component(ad->dssdev); 384 return ret; 385 } 386 387 ad->card = card; 388 snd_soc_card_set_drvdata(card, ad); 389 390 dev_set_drvdata(dev, ad); 391 392 return 0; 393} 394 395static int omap_hdmi_audio_remove(struct platform_device *pdev) 396{ 397 struct hdmi_audio_data *ad = platform_get_drvdata(pdev); 398 399 snd_soc_unregister_card(ad->card); 400 snd_soc_unregister_component(ad->dssdev); 401 return 0; 402} 403 404static struct platform_driver hdmi_audio_driver = { 405 .driver = { 406 .name = DRV_NAME, 407 }, 408 .probe = omap_hdmi_audio_probe, 409 .remove = omap_hdmi_audio_remove, 410}; 411 412module_platform_driver(hdmi_audio_driver); 413 414MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>"); 415MODULE_DESCRIPTION("OMAP HDMI Audio Driver"); 416MODULE_LICENSE("GPL"); 417MODULE_ALIAS("platform:" DRV_NAME); 418