Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 1 | /* |
| 2 | * intel_hdmi_lpe_audio.c - Intel HDMI LPE audio driver for Atom platforms |
| 3 | * |
| 4 | * Copyright (C) 2016 Intel Corp |
| 5 | * Authors: |
| 6 | * Jerome Anand <jerome.anand@intel.com> |
| 7 | * Aravind Siddappaji <aravindx.siddappaji@intel.com> |
| 8 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify |
| 11 | * it under the terms of the GNU General Public License as published by |
| 12 | * the Free Software Foundation; version 2 of the License. |
| 13 | * |
| 14 | * This program is distributed in the hope that it will be useful, but |
| 15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 17 | * General Public License for more details. |
| 18 | * |
| 19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 20 | */ |
| 21 | |
| 22 | #include <linux/platform_device.h> |
| 23 | #include <linux/irqreturn.h> |
| 24 | #include <linux/interrupt.h> |
| 25 | #include <linux/io.h> |
| 26 | #include <linux/slab.h> |
| 27 | #include <linux/module.h> |
| 28 | #include <linux/pci.h> |
| 29 | #include <sound/core.h> |
| 30 | #include <sound/pcm.h> |
| 31 | #include <sound/pcm_params.h> |
| 32 | #include <sound/initval.h> |
| 33 | #include <sound/control.h> |
| 34 | #include <sound/initval.h> |
| 35 | #include <drm/intel_lpe_audio.h> |
| 36 | #include "intel_hdmi_lpe_audio.h" |
Jerome Anand | 5dab11d | 2017-01-25 04:27:52 +0530 | [diff] [blame] | 37 | #include "intel_hdmi_audio.h" |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 38 | |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 39 | struct hdmi_lpe_audio_ctx { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 40 | struct platform_device *pdev; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 41 | int irq; |
| 42 | void __iomem *mmio_start; |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 43 | struct snd_intelhad *had; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 44 | int tmds_clock_speed; |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 45 | bool dp_output; |
| 46 | int link_rate; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 47 | unsigned int had_config_offset; |
| 48 | int hdmi_audio_interrupt_mask; |
| 49 | struct work_struct hdmi_audio_wq; |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 50 | int state; /* connection state */ |
Takashi Iwai | 7f2e9ab | 2017-01-30 18:15:40 +0100 | [diff] [blame] | 51 | union otm_hdmi_eld_t eld; /* ELD copy */ |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 52 | }; |
| 53 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 54 | static void mid_hdmi_audio_signal_event(struct platform_device *pdev, |
| 55 | enum had_event_type event) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 56 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 57 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 58 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 59 | dev_dbg(&pdev->dev, "%s: Enter\n", __func__); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 60 | |
| 61 | /* The handler is protected in the respective |
| 62 | * event handlers to avoid races |
| 63 | */ |
Takashi Iwai | 437af8f | 2017-01-30 17:38:00 +0100 | [diff] [blame] | 64 | had_event_handler(event, ctx->had); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 65 | } |
| 66 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 67 | /* |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 68 | * used to write into display controller HDMI audio registers. |
| 69 | */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 70 | int mid_hdmi_audio_write(struct platform_device *pdev, u32 reg, u32 val) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 71 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 72 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 73 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 74 | dev_dbg(&pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, reg, val); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 75 | |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 76 | if (ctx->dp_output) { |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 77 | if (reg == AUD_CONFIG && (val & AUD_CONFIG_VALID_BIT)) |
| 78 | val |= AUD_CONFIG_DP_MODE | AUD_CONFIG_BLOCK_BIT; |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 79 | } |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 80 | iowrite32(val, ctx->mmio_start + ctx->had_config_offset + reg); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 81 | |
| 82 | return 0; |
| 83 | } |
| 84 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 85 | /* |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 86 | * used to get the register value read from |
| 87 | * display controller HDMI audio registers. |
| 88 | */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 89 | int mid_hdmi_audio_read(struct platform_device *pdev, u32 reg, u32 *val) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 90 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 91 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 92 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 93 | *val = ioread32(ctx->mmio_start + ctx->had_config_offset + reg); |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 94 | dev_dbg(&pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, reg, *val); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 95 | return 0; |
| 96 | } |
| 97 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 98 | /* |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 99 | * used to update the masked bits in display controller HDMI |
| 100 | * audio registers. |
| 101 | */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 102 | int mid_hdmi_audio_rmw(struct platform_device *pdev, |
| 103 | u32 reg, u32 val, u32 mask) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 104 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 105 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 106 | u32 val_tmp = 0; |
| 107 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 108 | val_tmp = ioread32(ctx->mmio_start + ctx->had_config_offset + reg); |
| 109 | val_tmp &= ~mask; |
| 110 | val_tmp |= (val & mask); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 111 | |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 112 | if (ctx->dp_output) { |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 113 | if (reg == AUD_CONFIG && (val_tmp & AUD_CONFIG_VALID_BIT)) |
| 114 | val_tmp |= AUD_CONFIG_DP_MODE | AUD_CONFIG_BLOCK_BIT; |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 115 | } |
| 116 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 117 | iowrite32(val_tmp, ctx->mmio_start + ctx->had_config_offset + reg); |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 118 | dev_dbg(&pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 119 | reg, val_tmp); |
| 120 | |
| 121 | return 0; |
| 122 | } |
| 123 | |
Takashi Iwai | 9eca88c | 2017-01-30 16:37:06 +0100 | [diff] [blame] | 124 | /* |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 125 | * used to return the HDMI audio capabilities. |
| 126 | * e.g. resolution, frame rate. |
| 127 | */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 128 | int mid_hdmi_audio_get_caps(struct platform_device *pdev, |
| 129 | enum had_caps_list get_element, |
Takashi Iwai | 9eca88c | 2017-01-30 16:37:06 +0100 | [diff] [blame] | 130 | void *capabilities) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 131 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 132 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 133 | int ret = 0; |
| 134 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 135 | dev_dbg(&pdev->dev, "%s: Enter\n", __func__); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 136 | |
| 137 | switch (get_element) { |
| 138 | case HAD_GET_ELD: |
Takashi Iwai | 7f2e9ab | 2017-01-30 18:15:40 +0100 | [diff] [blame] | 139 | memcpy(capabilities, &ctx->eld, sizeof(ctx->eld)); |
| 140 | print_hex_dump_bytes("eld: ", DUMP_PREFIX_NONE, |
| 141 | (u8 *)&ctx->eld, sizeof(ctx->eld)); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 142 | break; |
| 143 | case HAD_GET_DISPLAY_RATE: |
| 144 | /* ToDo: Verify if sampling freq logic is correct */ |
| 145 | *(u32 *)capabilities = ctx->tmds_clock_speed; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 146 | dev_dbg(&pdev->dev, "%s: tmds_clock_speed = 0x%x\n", |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 147 | __func__, ctx->tmds_clock_speed); |
| 148 | break; |
| 149 | case HAD_GET_LINK_RATE: |
| 150 | /* ToDo: Verify if sampling freq logic is correct */ |
| 151 | *(u32 *)capabilities = ctx->link_rate; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 152 | dev_dbg(&pdev->dev, "%s: link rate = 0x%x\n", |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 153 | __func__, ctx->link_rate); |
| 154 | break; |
| 155 | case HAD_GET_DP_OUTPUT: |
| 156 | *(u32 *)capabilities = ctx->dp_output; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 157 | dev_dbg(&pdev->dev, "%s: dp_output = %d\n", |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 158 | __func__, ctx->dp_output); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 159 | break; |
| 160 | default: |
| 161 | break; |
| 162 | } |
| 163 | |
| 164 | return ret; |
| 165 | } |
| 166 | |
Takashi Iwai | 9eca88c | 2017-01-30 16:37:06 +0100 | [diff] [blame] | 167 | /* |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 168 | * used to set the HDMI audio capabilities. |
| 169 | * e.g. Audio INT. |
| 170 | */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 171 | int mid_hdmi_audio_set_caps(struct platform_device *pdev, |
| 172 | enum had_caps_list set_element, |
Takashi Iwai | 9eca88c | 2017-01-30 16:37:06 +0100 | [diff] [blame] | 173 | void *capabilties) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 174 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 175 | dev_dbg(&pdev->dev, "%s: cap_id = 0x%x\n", __func__, set_element); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 176 | |
| 177 | switch (set_element) { |
| 178 | case HAD_SET_ENABLE_AUDIO_INT: |
| 179 | { |
| 180 | u32 status_reg; |
| 181 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 182 | mid_hdmi_audio_read(pdev, AUD_HDMI_STATUS_v2, |
| 183 | &status_reg); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 184 | status_reg |= |
| 185 | HDMI_AUDIO_BUFFER_DONE | HDMI_AUDIO_UNDERRUN; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 186 | mid_hdmi_audio_write(pdev, AUD_HDMI_STATUS_v2, |
| 187 | status_reg); |
| 188 | mid_hdmi_audio_read(pdev, AUD_HDMI_STATUS_v2, |
| 189 | &status_reg); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 190 | } |
| 191 | break; |
| 192 | default: |
| 193 | break; |
| 194 | } |
| 195 | |
| 196 | return 0; |
| 197 | } |
| 198 | |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 199 | static void _had_wq(struct work_struct *work) |
| 200 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 201 | struct hdmi_lpe_audio_ctx *ctx = |
| 202 | container_of(work, struct hdmi_lpe_audio_ctx, hdmi_audio_wq); |
| 203 | |
| 204 | mid_hdmi_audio_signal_event(ctx->pdev, HAD_EVENT_HOT_PLUG); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 205 | } |
| 206 | |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 207 | static irqreturn_t display_pipe_interrupt_handler(int irq, void *dev_id) |
| 208 | { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 209 | struct platform_device *pdev = dev_id; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 210 | u32 audio_stat, audio_reg; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 211 | struct hdmi_lpe_audio_ctx *ctx; |
| 212 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 213 | dev_dbg(&pdev->dev, "%s: Enter\n", __func__); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 214 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 215 | ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 216 | |
Takashi Iwai | f23df80 | 2017-01-30 16:29:39 +0100 | [diff] [blame] | 217 | audio_reg = AUD_HDMI_STATUS_v2; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 218 | mid_hdmi_audio_read(pdev, audio_reg, &audio_stat); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 219 | |
| 220 | if (audio_stat & HDMI_AUDIO_UNDERRUN) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 221 | mid_hdmi_audio_write(pdev, audio_reg, HDMI_AUDIO_UNDERRUN); |
| 222 | mid_hdmi_audio_signal_event(pdev, |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 223 | HAD_EVENT_AUDIO_BUFFER_UNDERRUN); |
| 224 | } |
| 225 | |
| 226 | if (audio_stat & HDMI_AUDIO_BUFFER_DONE) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 227 | mid_hdmi_audio_write(pdev, audio_reg, HDMI_AUDIO_BUFFER_DONE); |
| 228 | mid_hdmi_audio_signal_event(pdev, |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 229 | HAD_EVENT_AUDIO_BUFFER_DONE); |
| 230 | } |
| 231 | |
| 232 | return IRQ_HANDLED; |
| 233 | } |
| 234 | |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 235 | static void notify_audio_lpe(struct platform_device *pdev) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 236 | { |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 237 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
| 238 | struct intel_hdmi_lpe_audio_pdata *pdata = pdev->dev.platform_data; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 239 | |
| 240 | if (pdata->hdmi_connected != true) { |
| 241 | |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 242 | dev_dbg(&pdev->dev, "%s: Event: HAD_NOTIFY_HOT_UNPLUG\n", |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 243 | __func__); |
| 244 | |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 245 | if (ctx->state == hdmi_connector_status_connected) { |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 246 | |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 247 | ctx->state = hdmi_connector_status_disconnected; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 248 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 249 | mid_hdmi_audio_signal_event(pdev, |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 250 | HAD_EVENT_HOT_UNPLUG); |
| 251 | } else |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 252 | dev_dbg(&pdev->dev, "%s: Already Unplugged!\n", |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 253 | __func__); |
| 254 | |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 255 | } else { |
| 256 | struct intel_hdmi_lpe_audio_eld *eld = &pdata->eld; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 257 | |
Pierre-Louis Bossart | 0843e04 | 2017-01-31 14:16:53 -0600 | [diff] [blame] | 258 | switch (eld->pipe_id) { |
| 259 | case 0: |
| 260 | ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; |
| 261 | break; |
| 262 | case 1: |
| 263 | ctx->had_config_offset = AUDIO_HDMI_CONFIG_B; |
| 264 | break; |
| 265 | case 2: |
| 266 | ctx->had_config_offset = AUDIO_HDMI_CONFIG_C; |
| 267 | break; |
| 268 | default: |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 269 | dev_dbg(&pdev->dev, "Invalid pipe %d\n", |
Pierre-Louis Bossart | 0843e04 | 2017-01-31 14:16:53 -0600 | [diff] [blame] | 270 | eld->pipe_id); |
| 271 | break; |
| 272 | } |
| 273 | |
Takashi Iwai | 7f2e9ab | 2017-01-30 18:15:40 +0100 | [diff] [blame] | 274 | memcpy(&ctx->eld, eld->eld_data, sizeof(ctx->eld)); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 275 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 276 | mid_hdmi_audio_signal_event(pdev, HAD_EVENT_HOT_PLUG); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 277 | |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 278 | ctx->state = hdmi_connector_status_connected; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 279 | |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 280 | dev_dbg(&pdev->dev, "%s: HAD_NOTIFY_ELD : port = %d, tmds = %d\n", |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 281 | __func__, eld->port_id, pdata->tmds_clock_speed); |
| 282 | |
| 283 | if (pdata->tmds_clock_speed) { |
| 284 | ctx->tmds_clock_speed = pdata->tmds_clock_speed; |
Pierre-Louis Bossart | 964ca80 | 2017-01-31 14:16:52 -0600 | [diff] [blame] | 285 | ctx->dp_output = pdata->dp_output; |
| 286 | ctx->link_rate = pdata->link_rate; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 287 | mid_hdmi_audio_signal_event(pdev, |
| 288 | HAD_EVENT_MODE_CHANGING); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 289 | } |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 290 | } |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 291 | } |
| 292 | |
| 293 | /** |
| 294 | * hdmi_lpe_audio_probe - start bridge with i915 |
| 295 | * |
Jerome Anand | 5dab11d | 2017-01-25 04:27:52 +0530 | [diff] [blame] | 296 | * This function is called when the i915 driver creates the |
| 297 | * hdmi-lpe-audio platform device. Card creation is deferred until a |
| 298 | * hot plug event is received |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 299 | */ |
| 300 | static int hdmi_lpe_audio_probe(struct platform_device *pdev) |
| 301 | { |
| 302 | struct hdmi_lpe_audio_ctx *ctx; |
| 303 | struct intel_hdmi_lpe_audio_pdata *pdata; |
| 304 | int irq; |
| 305 | struct resource *res_mmio; |
| 306 | void __iomem *mmio_start; |
| 307 | int ret = 0; |
| 308 | unsigned long flag_irq; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 309 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 310 | dev_dbg(&pdev->dev, "Enter %s\n", __func__); |
Takashi Iwai | dae15a9 | 2017-01-31 08:02:16 +0100 | [diff] [blame] | 311 | dev_dbg(&pdev->dev, "dma_mask: %p\n", pdev->dev.dma_mask); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 312 | |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 313 | /* get resources */ |
| 314 | irq = platform_get_irq(pdev, 0); |
| 315 | if (irq < 0) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 316 | dev_err(&pdev->dev, "Could not get irq resource\n"); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 317 | return -ENODEV; |
| 318 | } |
| 319 | |
| 320 | res_mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 321 | if (!res_mmio) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 322 | dev_err(&pdev->dev, "Could not get IO_MEM resources\n"); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 323 | return -ENXIO; |
| 324 | } |
| 325 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 326 | dev_dbg(&pdev->dev, "%s: mmio_start = 0x%x, mmio_end = 0x%x\n", |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 327 | __func__, (unsigned int)res_mmio->start, |
| 328 | (unsigned int)res_mmio->end); |
| 329 | |
| 330 | mmio_start = ioremap_nocache(res_mmio->start, |
kbuild test robot | f0fd412 | 2017-01-24 23:41:46 +0800 | [diff] [blame] | 331 | (size_t)(resource_size(res_mmio))); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 332 | if (!mmio_start) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 333 | dev_err(&pdev->dev, "Could not get ioremap\n"); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 334 | return -EACCES; |
| 335 | } |
| 336 | |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 337 | /* alloc and save context */ |
| 338 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
| 339 | if (ctx == NULL) { |
Takashi Iwai | 45459d1 | 2017-01-31 08:29:56 +0100 | [diff] [blame] | 340 | ret = -ENOMEM; |
| 341 | goto error_ctx; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 342 | } |
| 343 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 344 | ctx->pdev = pdev; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 345 | ctx->irq = irq; |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 346 | dev_dbg(&pdev->dev, "hdmi lpe audio: irq num = %d\n", irq); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 347 | ctx->mmio_start = mmio_start; |
| 348 | ctx->tmds_clock_speed = DIS_SAMPLE_RATE_148_5; |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 349 | INIT_WORK(&ctx->hdmi_audio_wq, _had_wq); |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 350 | ctx->state = hdmi_connector_status_disconnected; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 351 | |
Pierre-Louis Bossart | 0843e04 | 2017-01-31 14:16:53 -0600 | [diff] [blame] | 352 | /* assume pipe A as default */ |
| 353 | ctx->had_config_offset = AUDIO_HDMI_CONFIG_A; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 354 | |
| 355 | pdata = pdev->dev.platform_data; |
| 356 | |
| 357 | if (pdata == NULL) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 358 | dev_err(&pdev->dev, "%s: quit: pdata not allocated by i915!!\n", __func__); |
Takashi Iwai | 45459d1 | 2017-01-31 08:29:56 +0100 | [diff] [blame] | 359 | ret = -ENOMEM; |
Takashi Iwai | 033e925 | 2017-01-30 17:40:04 +0100 | [diff] [blame] | 360 | goto error_irq; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 361 | } |
| 362 | |
| 363 | platform_set_drvdata(pdev, ctx); |
| 364 | |
Takashi Iwai | 033e925 | 2017-01-30 17:40:04 +0100 | [diff] [blame] | 365 | /* setup interrupt handler */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 366 | ret = request_irq(irq, display_pipe_interrupt_handler, 0, |
| 367 | pdev->name, pdev); |
| 368 | |
Takashi Iwai | 033e925 | 2017-01-30 17:40:04 +0100 | [diff] [blame] | 369 | if (ret < 0) { |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 370 | dev_err(&pdev->dev, "request_irq failed\n"); |
Takashi Iwai | 033e925 | 2017-01-30 17:40:04 +0100 | [diff] [blame] | 371 | goto error_irq; |
| 372 | } |
| 373 | |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 374 | ret = hdmi_audio_probe(pdev, &ctx->had); |
Takashi Iwai | 45459d1 | 2017-01-31 08:29:56 +0100 | [diff] [blame] | 375 | if (ret < 0) |
| 376 | goto error_probe; |
| 377 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 378 | dev_dbg(&pdev->dev, "hdmi lpe audio: setting pin eld notify callback\n"); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 379 | |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 380 | /* The Audio driver is loading now and we need to notify |
| 381 | * it if there is an HDMI device attached |
| 382 | */ |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 383 | dev_dbg(&pdev->dev, "%s: Scheduling HDMI audio work queue\n", |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 384 | __func__); |
| 385 | schedule_work(&ctx->hdmi_audio_wq); |
| 386 | |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 387 | spin_lock_irqsave(&pdata->lpe_audio_slock, flag_irq); |
| 388 | pdata->notify_audio_lpe = notify_audio_lpe; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 389 | if (pdata->notify_pending) { |
| 390 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 391 | dev_dbg(&pdev->dev, "%s: handle pending notification\n", __func__); |
Takashi Iwai | b1c01f4 | 2017-01-30 17:56:39 +0100 | [diff] [blame] | 392 | notify_audio_lpe(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 393 | pdata->notify_pending = false; |
| 394 | } |
| 395 | spin_unlock_irqrestore(&pdata->lpe_audio_slock, flag_irq); |
| 396 | |
| 397 | return ret; |
Takashi Iwai | 45459d1 | 2017-01-31 08:29:56 +0100 | [diff] [blame] | 398 | |
| 399 | error_probe: |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 400 | free_irq(irq, pdev); |
Takashi Iwai | 45459d1 | 2017-01-31 08:29:56 +0100 | [diff] [blame] | 401 | error_irq: |
Takashi Iwai | 033e925 | 2017-01-30 17:40:04 +0100 | [diff] [blame] | 402 | kfree(ctx); |
| 403 | error_ctx: |
Takashi Iwai | 45459d1 | 2017-01-31 08:29:56 +0100 | [diff] [blame] | 404 | iounmap(mmio_start); |
| 405 | return ret; |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 406 | } |
| 407 | |
| 408 | /** |
| 409 | * hdmi_lpe_audio_remove - stop bridge with i915 |
| 410 | * |
| 411 | * This function is called when the platform device is destroyed. The sound |
| 412 | * card should have been removed on hot plug event. |
| 413 | */ |
| 414 | static int hdmi_lpe_audio_remove(struct platform_device *pdev) |
| 415 | { |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 416 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 417 | |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 418 | dev_dbg(&pdev->dev, "Enter %s\n", __func__); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 419 | |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 420 | hdmi_audio_remove(ctx->had); |
Jerome Anand | 5dab11d | 2017-01-25 04:27:52 +0530 | [diff] [blame] | 421 | |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 422 | /* release resources */ |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 423 | iounmap(ctx->mmio_start); |
Takashi Iwai | bf8b24f | 2017-01-30 18:09:01 +0100 | [diff] [blame] | 424 | free_irq(ctx->irq, pdev); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 425 | kfree(ctx); |
| 426 | return 0; |
| 427 | } |
| 428 | |
Takashi Iwai | 6f9ecc7 | 2017-01-30 16:52:06 +0100 | [diff] [blame] | 429 | static int hdmi_lpe_audio_suspend(struct platform_device *pdev, |
| 430 | pm_message_t state) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 431 | { |
Takashi Iwai | 6f9ecc7 | 2017-01-30 16:52:06 +0100 | [diff] [blame] | 432 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
| 433 | |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 434 | dev_dbg(&pdev->dev, "%s: state %d", __func__, ctx->state); |
Takashi Iwai | 6f9ecc7 | 2017-01-30 16:52:06 +0100 | [diff] [blame] | 435 | /* HDMI is not connected, assuming audio device is suspended already */ |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 436 | if (ctx->state != hdmi_connector_status_disconnected) |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 437 | hdmi_audio_suspend(ctx->had); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 438 | return 0; |
| 439 | } |
| 440 | |
Takashi Iwai | 6f9ecc7 | 2017-01-30 16:52:06 +0100 | [diff] [blame] | 441 | static int hdmi_lpe_audio_resume(struct platform_device *pdev) |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 442 | { |
Takashi Iwai | 6f9ecc7 | 2017-01-30 16:52:06 +0100 | [diff] [blame] | 443 | struct hdmi_lpe_audio_ctx *ctx = platform_get_drvdata(pdev); |
| 444 | |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 445 | dev_dbg(&pdev->dev, "%s: state %d", __func__, ctx->state); |
Takashi Iwai | 6f9ecc7 | 2017-01-30 16:52:06 +0100 | [diff] [blame] | 446 | /* HDMI is not connected, there is no need to resume audio device */ |
Takashi Iwai | 055610b | 2017-01-30 18:11:13 +0100 | [diff] [blame] | 447 | if (ctx->state != hdmi_connector_status_disconnected) |
Takashi Iwai | 79dda75 | 2017-01-30 17:23:39 +0100 | [diff] [blame] | 448 | hdmi_audio_resume(ctx->had); |
Jerome Anand | 287599c | 2017-01-25 04:27:51 +0530 | [diff] [blame] | 449 | return 0; |
| 450 | } |
| 451 | |
| 452 | static struct platform_driver hdmi_lpe_audio_driver = { |
| 453 | .driver = { |
| 454 | .name = "hdmi-lpe-audio", |
| 455 | }, |
| 456 | .probe = hdmi_lpe_audio_probe, |
| 457 | .remove = hdmi_lpe_audio_remove, |
| 458 | .suspend = hdmi_lpe_audio_suspend, |
| 459 | .resume = hdmi_lpe_audio_resume |
| 460 | }; |
| 461 | |
| 462 | module_platform_driver(hdmi_lpe_audio_driver); |
| 463 | MODULE_LICENSE("GPL v2"); |
| 464 | MODULE_ALIAS("platform:hdmi_lpe_audio"); |