root/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dw_hdmi_writel
  2. dw_hdmi_reformat_iec958
  3. parity
  4. dw_hdmi_reformat_s24
  5. dw_hdmi_create_cs
  6. dw_hdmi_start_dma
  7. dw_hdmi_stop_dma
  8. snd_dw_hdmi_irq
  9. dw_hdmi_open
  10. dw_hdmi_close
  11. dw_hdmi_hw_free
  12. dw_hdmi_hw_params
  13. dw_hdmi_prepare
  14. dw_hdmi_trigger
  15. dw_hdmi_pointer
  16. snd_dw_hdmi_probe
  17. snd_dw_hdmi_remove
  18. snd_dw_hdmi_suspend
  19. snd_dw_hdmi_resume

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * DesignWare HDMI audio driver
   4  *
   5  * Written and tested against the Designware HDMI Tx found in iMX6.
   6  */
   7 #include <linux/io.h>
   8 #include <linux/interrupt.h>
   9 #include <linux/module.h>
  10 #include <linux/platform_device.h>
  11 #include <drm/bridge/dw_hdmi.h>
  12 #include <drm/drm_edid.h>
  13 
  14 #include <sound/asoundef.h>
  15 #include <sound/core.h>
  16 #include <sound/initval.h>
  17 #include <sound/pcm.h>
  18 #include <sound/pcm_drm_eld.h>
  19 #include <sound/pcm_iec958.h>
  20 
  21 #include "dw-hdmi-audio.h"
  22 
  23 #define DRIVER_NAME "dw-hdmi-ahb-audio"
  24 
  25 /* Provide some bits rather than bit offsets */
  26 enum {
  27         HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
  28         HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
  29         HDMI_AHB_DMA_START_START = BIT(0),
  30         HDMI_AHB_DMA_STOP_STOP = BIT(0),
  31         HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
  32         HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
  33         HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
  34         HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
  35         HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
  36         HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
  37         HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
  38                 HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
  39                 HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
  40                 HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
  41                 HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
  42                 HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
  43                 HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
  44         HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
  45         HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
  46         HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
  47         HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
  48         HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
  49         HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
  50         HDMI_IH_AHBDMAAUD_STAT0_ALL =
  51                 HDMI_IH_AHBDMAAUD_STAT0_ERROR |
  52                 HDMI_IH_AHBDMAAUD_STAT0_LOST |
  53                 HDMI_IH_AHBDMAAUD_STAT0_RETRY |
  54                 HDMI_IH_AHBDMAAUD_STAT0_DONE |
  55                 HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
  56                 HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
  57         HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
  58         HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
  59         HDMI_AHB_DMA_CONF0_INCR4 = 0,
  60         HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
  61         HDMI_AHB_DMA_MASK_DONE = BIT(7),
  62 
  63         HDMI_REVISION_ID = 0x0001,
  64         HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
  65         HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
  66         HDMI_AHB_DMA_CONF0 = 0x3600,
  67         HDMI_AHB_DMA_START = 0x3601,
  68         HDMI_AHB_DMA_STOP = 0x3602,
  69         HDMI_AHB_DMA_THRSLD = 0x3603,
  70         HDMI_AHB_DMA_STRADDR0 = 0x3604,
  71         HDMI_AHB_DMA_STPADDR0 = 0x3608,
  72         HDMI_AHB_DMA_MASK = 0x3614,
  73         HDMI_AHB_DMA_POL = 0x3615,
  74         HDMI_AHB_DMA_CONF1 = 0x3616,
  75         HDMI_AHB_DMA_BUFFPOL = 0x361a,
  76 };
  77 
  78 struct dw_hdmi_channel_conf {
  79         u8 conf1;
  80         u8 ca;
  81 };
  82 
  83 /*
  84  * The default mapping of ALSA channels to HDMI channels and speaker
  85  * allocation bits.  Note that we can't do channel remapping here -
  86  * channels must be in the same order.
  87  *
  88  * Mappings for alsa-lib pcm/surround*.conf files:
  89  *
  90  *              Front   Sur4.0  Sur4.1  Sur5.0  Sur5.1  Sur7.1
  91  * Channels     2       4       6       6       6       8
  92  *
  93  * Our mapping from ALSA channel to CEA686D speaker name and HDMI channel:
  94  *
  95  *                              Number of ALSA channels
  96  * ALSA Channel 2       3       4       5       6       7       8
  97  * 0            FL:0    =       =       =       =       =       =
  98  * 1            FR:1    =       =       =       =       =       =
  99  * 2                    FC:3    RL:4    LFE:2   =       =       =
 100  * 3                            RR:5    RL:4    FC:3    =       =
 101  * 4                                    RR:5    RL:4    =       =
 102  * 5                                            RR:5    =       =
 103  * 6                                                    RC:6    =
 104  * 7                                                    RLC/FRC RLC/FRC
 105  */
 106 static struct dw_hdmi_channel_conf default_hdmi_channel_config[7] = {
 107         { 0x03, 0x00 }, /* FL,FR */
 108         { 0x0b, 0x02 }, /* FL,FR,FC */
 109         { 0x33, 0x08 }, /* FL,FR,RL,RR */
 110         { 0x37, 0x09 }, /* FL,FR,LFE,RL,RR */
 111         { 0x3f, 0x0b }, /* FL,FR,LFE,FC,RL,RR */
 112         { 0x7f, 0x0f }, /* FL,FR,LFE,FC,RL,RR,RC */
 113         { 0xff, 0x13 }, /* FL,FR,LFE,FC,RL,RR,[FR]RC,[FR]LC */
 114 };
 115 
 116 struct snd_dw_hdmi {
 117         struct snd_card *card;
 118         struct snd_pcm *pcm;
 119         spinlock_t lock;
 120         struct dw_hdmi_audio_data data;
 121         struct snd_pcm_substream *substream;
 122         void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
 123         void *buf_src;
 124         void *buf_dst;
 125         dma_addr_t buf_addr;
 126         unsigned buf_offset;
 127         unsigned buf_period;
 128         unsigned buf_size;
 129         unsigned channels;
 130         u8 revision;
 131         u8 iec_offset;
 132         u8 cs[192][8];
 133 };
 134 
 135 static void dw_hdmi_writel(u32 val, void __iomem *ptr)
 136 {
 137         writeb_relaxed(val, ptr);
 138         writeb_relaxed(val >> 8, ptr + 1);
 139         writeb_relaxed(val >> 16, ptr + 2);
 140         writeb_relaxed(val >> 24, ptr + 3);
 141 }
 142 
 143 /*
 144  * Convert to hardware format: The userspace buffer contains IEC958 samples,
 145  * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
 146  * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
 147  * samples in 23..0.
 148  *
 149  * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
 150  *
 151  * Ideally, we could do with having the data properly formatted in userspace.
 152  */
 153 static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
 154         size_t offset, size_t bytes)
 155 {
 156         u32 *src = dw->buf_src + offset;
 157         u32 *dst = dw->buf_dst + offset;
 158         u32 *end = dw->buf_src + offset + bytes;
 159 
 160         do {
 161                 u32 b, sample = *src++;
 162 
 163                 b = (sample & 8) << (28 - 3);
 164 
 165                 sample >>= 4;
 166 
 167                 *dst++ = sample | b;
 168         } while (src < end);
 169 }
 170 
 171 static u32 parity(u32 sample)
 172 {
 173         sample ^= sample >> 16;
 174         sample ^= sample >> 8;
 175         sample ^= sample >> 4;
 176         sample ^= sample >> 2;
 177         sample ^= sample >> 1;
 178         return (sample & 1) << 27;
 179 }
 180 
 181 static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
 182         size_t offset, size_t bytes)
 183 {
 184         u32 *src = dw->buf_src + offset;
 185         u32 *dst = dw->buf_dst + offset;
 186         u32 *end = dw->buf_src + offset + bytes;
 187 
 188         do {
 189                 unsigned i;
 190                 u8 *cs;
 191 
 192                 cs = dw->cs[dw->iec_offset++];
 193                 if (dw->iec_offset >= 192)
 194                         dw->iec_offset = 0;
 195 
 196                 i = dw->channels;
 197                 do {
 198                         u32 sample = *src++;
 199 
 200                         sample &= ~0xff000000;
 201                         sample |= *cs++ << 24;
 202                         sample |= parity(sample & ~0xf8000000);
 203 
 204                         *dst++ = sample;
 205                 } while (--i);
 206         } while (src < end);
 207 }
 208 
 209 static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
 210         struct snd_pcm_runtime *runtime)
 211 {
 212         u8 cs[4];
 213         unsigned ch, i, j;
 214 
 215         snd_pcm_create_iec958_consumer(runtime, cs, sizeof(cs));
 216 
 217         memset(dw->cs, 0, sizeof(dw->cs));
 218 
 219         for (ch = 0; ch < 8; ch++) {
 220                 cs[2] &= ~IEC958_AES2_CON_CHANNEL;
 221                 cs[2] |= (ch + 1) << 4;
 222 
 223                 for (i = 0; i < ARRAY_SIZE(cs); i++) {
 224                         unsigned c = cs[i];
 225 
 226                         for (j = 0; j < 8; j++, c >>= 1)
 227                                 dw->cs[i * 8 + j][ch] = (c & 1) << 2;
 228                 }
 229         }
 230         dw->cs[0][0] |= BIT(4);
 231 }
 232 
 233 static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
 234 {
 235         void __iomem *base = dw->data.base;
 236         unsigned offset = dw->buf_offset;
 237         unsigned period = dw->buf_period;
 238         u32 start, stop;
 239 
 240         dw->reformat(dw, offset, period);
 241 
 242         /* Clear all irqs before enabling irqs and starting DMA */
 243         writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
 244                        base + HDMI_IH_AHBDMAAUD_STAT0);
 245 
 246         start = dw->buf_addr + offset;
 247         stop = start + period - 1;
 248 
 249         /* Setup the hardware start/stop addresses */
 250         dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
 251         dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
 252 
 253         writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
 254         writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
 255 
 256         offset += period;
 257         if (offset >= dw->buf_size)
 258                 offset = 0;
 259         dw->buf_offset = offset;
 260 }
 261 
 262 static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
 263 {
 264         /* Disable interrupts before disabling DMA */
 265         writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
 266         writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
 267 }
 268 
 269 static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
 270 {
 271         struct snd_dw_hdmi *dw = data;
 272         struct snd_pcm_substream *substream;
 273         unsigned stat;
 274 
 275         stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
 276         if (!stat)
 277                 return IRQ_NONE;
 278 
 279         writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
 280 
 281         substream = dw->substream;
 282         if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
 283                 snd_pcm_period_elapsed(substream);
 284 
 285                 spin_lock(&dw->lock);
 286                 if (dw->substream)
 287                         dw_hdmi_start_dma(dw);
 288                 spin_unlock(&dw->lock);
 289         }
 290 
 291         return IRQ_HANDLED;
 292 }
 293 
 294 static struct snd_pcm_hardware dw_hdmi_hw = {
 295         .info = SNDRV_PCM_INFO_INTERLEAVED |
 296                 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 297                 SNDRV_PCM_INFO_MMAP |
 298                 SNDRV_PCM_INFO_MMAP_VALID,
 299         .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
 300                    SNDRV_PCM_FMTBIT_S24_LE,
 301         .rates = SNDRV_PCM_RATE_32000 |
 302                  SNDRV_PCM_RATE_44100 |
 303                  SNDRV_PCM_RATE_48000 |
 304                  SNDRV_PCM_RATE_88200 |
 305                  SNDRV_PCM_RATE_96000 |
 306                  SNDRV_PCM_RATE_176400 |
 307                  SNDRV_PCM_RATE_192000,
 308         .channels_min = 2,
 309         .channels_max = 8,
 310         .buffer_bytes_max = 1024 * 1024,
 311         .period_bytes_min = 256,
 312         .period_bytes_max = 8192,       /* ERR004323: must limit to 8k */
 313         .periods_min = 2,
 314         .periods_max = 16,
 315         .fifo_size = 0,
 316 };
 317 
 318 static int dw_hdmi_open(struct snd_pcm_substream *substream)
 319 {
 320         struct snd_pcm_runtime *runtime = substream->runtime;
 321         struct snd_dw_hdmi *dw = substream->private_data;
 322         void __iomem *base = dw->data.base;
 323         int ret;
 324 
 325         runtime->hw = dw_hdmi_hw;
 326 
 327         ret = snd_pcm_hw_constraint_eld(runtime, dw->data.eld);
 328         if (ret < 0)
 329                 return ret;
 330 
 331         ret = snd_pcm_limit_hw_rates(runtime);
 332         if (ret < 0)
 333                 return ret;
 334 
 335         ret = snd_pcm_hw_constraint_integer(runtime,
 336                                             SNDRV_PCM_HW_PARAM_PERIODS);
 337         if (ret < 0)
 338                 return ret;
 339 
 340         /* Limit the buffer size to the size of the preallocated buffer */
 341         ret = snd_pcm_hw_constraint_minmax(runtime,
 342                                            SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
 343                                            0, substream->dma_buffer.bytes);
 344         if (ret < 0)
 345                 return ret;
 346 
 347         /* Clear FIFO */
 348         writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
 349                        base + HDMI_AHB_DMA_CONF0);
 350 
 351         /* Configure interrupt polarities */
 352         writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
 353         writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
 354 
 355         /* Keep interrupts masked, and clear any pending */
 356         writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
 357         writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
 358 
 359         ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
 360                           "dw-hdmi-audio", dw);
 361         if (ret)
 362                 return ret;
 363 
 364         /* Un-mute done interrupt */
 365         writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
 366                        ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
 367                        base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 368 
 369         return 0;
 370 }
 371 
 372 static int dw_hdmi_close(struct snd_pcm_substream *substream)
 373 {
 374         struct snd_dw_hdmi *dw = substream->private_data;
 375 
 376         /* Mute all interrupts */
 377         writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
 378                        dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 379 
 380         free_irq(dw->data.irq, dw);
 381 
 382         return 0;
 383 }
 384 
 385 static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
 386 {
 387         return snd_pcm_lib_free_vmalloc_buffer(substream);
 388 }
 389 
 390 static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
 391         struct snd_pcm_hw_params *params)
 392 {
 393         /* Allocate the PCM runtime buffer, which is exposed to userspace. */
 394         return snd_pcm_lib_alloc_vmalloc_buffer(substream,
 395                                                 params_buffer_bytes(params));
 396 }
 397 
 398 static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 399 {
 400         struct snd_pcm_runtime *runtime = substream->runtime;
 401         struct snd_dw_hdmi *dw = substream->private_data;
 402         u8 threshold, conf0, conf1, ca;
 403 
 404         /* Setup as per 3.0.5 FSL 4.1.0 BSP */
 405         switch (dw->revision) {
 406         case 0x0a:
 407                 conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
 408                         HDMI_AHB_DMA_CONF0_INCR4;
 409                 if (runtime->channels == 2)
 410                         threshold = 126;
 411                 else
 412                         threshold = 124;
 413                 break;
 414         case 0x1a:
 415                 conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
 416                         HDMI_AHB_DMA_CONF0_INCR8;
 417                 threshold = 128;
 418                 break;
 419         default:
 420                 /* NOTREACHED */
 421                 return -EINVAL;
 422         }
 423 
 424         dw_hdmi_set_sample_rate(dw->data.hdmi, runtime->rate);
 425 
 426         /* Minimum number of bytes in the fifo. */
 427         runtime->hw.fifo_size = threshold * 32;
 428 
 429         conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
 430         conf1 = default_hdmi_channel_config[runtime->channels - 2].conf1;
 431         ca = default_hdmi_channel_config[runtime->channels - 2].ca;
 432 
 433         writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
 434         writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
 435         writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
 436 
 437         dw_hdmi_set_channel_count(dw->data.hdmi, runtime->channels);
 438         dw_hdmi_set_channel_allocation(dw->data.hdmi, ca);
 439 
 440         switch (runtime->format) {
 441         case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
 442                 dw->reformat = dw_hdmi_reformat_iec958;
 443                 break;
 444         case SNDRV_PCM_FORMAT_S24_LE:
 445                 dw_hdmi_create_cs(dw, runtime);
 446                 dw->reformat = dw_hdmi_reformat_s24;
 447                 break;
 448         }
 449         dw->iec_offset = 0;
 450         dw->channels = runtime->channels;
 451         dw->buf_src  = runtime->dma_area;
 452         dw->buf_dst  = substream->dma_buffer.area;
 453         dw->buf_addr = substream->dma_buffer.addr;
 454         dw->buf_period = snd_pcm_lib_period_bytes(substream);
 455         dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
 456 
 457         return 0;
 458 }
 459 
 460 static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 461 {
 462         struct snd_dw_hdmi *dw = substream->private_data;
 463         unsigned long flags;
 464         int ret = 0;
 465 
 466         switch (cmd) {
 467         case SNDRV_PCM_TRIGGER_START:
 468                 spin_lock_irqsave(&dw->lock, flags);
 469                 dw->buf_offset = 0;
 470                 dw->substream = substream;
 471                 dw_hdmi_start_dma(dw);
 472                 dw_hdmi_audio_enable(dw->data.hdmi);
 473                 spin_unlock_irqrestore(&dw->lock, flags);
 474                 substream->runtime->delay = substream->runtime->period_size;
 475                 break;
 476 
 477         case SNDRV_PCM_TRIGGER_STOP:
 478                 spin_lock_irqsave(&dw->lock, flags);
 479                 dw->substream = NULL;
 480                 dw_hdmi_stop_dma(dw);
 481                 dw_hdmi_audio_disable(dw->data.hdmi);
 482                 spin_unlock_irqrestore(&dw->lock, flags);
 483                 break;
 484 
 485         default:
 486                 ret = -EINVAL;
 487                 break;
 488         }
 489 
 490         return ret;
 491 }
 492 
 493 static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
 494 {
 495         struct snd_pcm_runtime *runtime = substream->runtime;
 496         struct snd_dw_hdmi *dw = substream->private_data;
 497 
 498         /*
 499          * We are unable to report the exact hardware position as
 500          * reading the 32-bit DMA position using 8-bit reads is racy.
 501          */
 502         return bytes_to_frames(runtime, dw->buf_offset);
 503 }
 504 
 505 static const struct snd_pcm_ops snd_dw_hdmi_ops = {
 506         .open = dw_hdmi_open,
 507         .close = dw_hdmi_close,
 508         .ioctl = snd_pcm_lib_ioctl,
 509         .hw_params = dw_hdmi_hw_params,
 510         .hw_free = dw_hdmi_hw_free,
 511         .prepare = dw_hdmi_prepare,
 512         .trigger = dw_hdmi_trigger,
 513         .pointer = dw_hdmi_pointer,
 514         .page = snd_pcm_lib_get_vmalloc_page,
 515 };
 516 
 517 static int snd_dw_hdmi_probe(struct platform_device *pdev)
 518 {
 519         const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
 520         struct device *dev = pdev->dev.parent;
 521         struct snd_dw_hdmi *dw;
 522         struct snd_card *card;
 523         struct snd_pcm *pcm;
 524         unsigned revision;
 525         int ret;
 526 
 527         writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
 528                        data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 529         revision = readb_relaxed(data->base + HDMI_REVISION_ID);
 530         if (revision != 0x0a && revision != 0x1a) {
 531                 dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
 532                         revision);
 533                 return -ENXIO;
 534         }
 535 
 536         ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
 537                               THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
 538         if (ret < 0)
 539                 return ret;
 540 
 541         strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
 542         strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
 543         snprintf(card->longname, sizeof(card->longname),
 544                  "%s rev 0x%02x, irq %d", card->shortname, revision,
 545                  data->irq);
 546 
 547         dw = card->private_data;
 548         dw->card = card;
 549         dw->data = *data;
 550         dw->revision = revision;
 551 
 552         spin_lock_init(&dw->lock);
 553 
 554         ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
 555         if (ret < 0)
 556                 goto err;
 557 
 558         dw->pcm = pcm;
 559         pcm->private_data = dw;
 560         strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
 561         snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
 562 
 563         /*
 564          * To support 8-channel 96kHz audio reliably, we need 512k
 565          * to satisfy alsa with our restricted period (ERR004323).
 566          */
 567         snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
 568                         dev, 128 * 1024, 1024 * 1024);
 569 
 570         ret = snd_card_register(card);
 571         if (ret < 0)
 572                 goto err;
 573 
 574         platform_set_drvdata(pdev, dw);
 575 
 576         return 0;
 577 
 578 err:
 579         snd_card_free(card);
 580         return ret;
 581 }
 582 
 583 static int snd_dw_hdmi_remove(struct platform_device *pdev)
 584 {
 585         struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
 586 
 587         snd_card_free(dw->card);
 588 
 589         return 0;
 590 }
 591 
 592 #if defined(CONFIG_PM_SLEEP) && defined(IS_NOT_BROKEN)
 593 /*
 594  * This code is fine, but requires implementation in the dw_hdmi_trigger()
 595  * method which is currently missing as I have no way to test this.
 596  */
 597 static int snd_dw_hdmi_suspend(struct device *dev)
 598 {
 599         struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
 600 
 601         snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
 602 
 603         return 0;
 604 }
 605 
 606 static int snd_dw_hdmi_resume(struct device *dev)
 607 {
 608         struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
 609 
 610         snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
 611 
 612         return 0;
 613 }
 614 
 615 static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
 616                          snd_dw_hdmi_resume);
 617 #define PM_OPS &snd_dw_hdmi_pm
 618 #else
 619 #define PM_OPS NULL
 620 #endif
 621 
 622 static struct platform_driver snd_dw_hdmi_driver = {
 623         .probe  = snd_dw_hdmi_probe,
 624         .remove = snd_dw_hdmi_remove,
 625         .driver = {
 626                 .name = DRIVER_NAME,
 627                 .pm = PM_OPS,
 628         },
 629 };
 630 
 631 module_platform_driver(snd_dw_hdmi_driver);
 632 
 633 MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
 634 MODULE_DESCRIPTION("Synopsis Designware HDMI AHB ALSA interface");
 635 MODULE_LICENSE("GPL v2");
 636 MODULE_ALIAS("platform:" DRIVER_NAME);

/* [<][>][^][v][top][bottom][index][help] */