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