1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * HDMI wrapper 4 * 5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ 6 */ 7 8 #define DSS_SUBSYS_NAME "HDMIWP" 9 10 #include <linux/kernel.h> 11 #include <linux/err.h> 12 #include <linux/io.h> 13 #include <linux/platform_device.h> 14 #include <linux/seq_file.h> 15 16 #include "omapdss.h" 17 #include "dss.h" 18 #include "hdmi.h" 19 20 void hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s) 21 { 22 #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r)) 23 24 DUMPREG(HDMI_WP_REVISION); 25 DUMPREG(HDMI_WP_SYSCONFIG); 26 DUMPREG(HDMI_WP_IRQSTATUS_RAW); 27 DUMPREG(HDMI_WP_IRQSTATUS); 28 DUMPREG(HDMI_WP_IRQENABLE_SET); 29 DUMPREG(HDMI_WP_IRQENABLE_CLR); 30 DUMPREG(HDMI_WP_IRQWAKEEN); 31 DUMPREG(HDMI_WP_PWR_CTRL); 32 DUMPREG(HDMI_WP_DEBOUNCE); 33 DUMPREG(HDMI_WP_VIDEO_CFG); 34 DUMPREG(HDMI_WP_VIDEO_SIZE); 35 DUMPREG(HDMI_WP_VIDEO_TIMING_H); 36 DUMPREG(HDMI_WP_VIDEO_TIMING_V); 37 DUMPREG(HDMI_WP_CLK); 38 DUMPREG(HDMI_WP_AUDIO_CFG); 39 DUMPREG(HDMI_WP_AUDIO_CFG2); 40 DUMPREG(HDMI_WP_AUDIO_CTRL); 41 DUMPREG(HDMI_WP_AUDIO_DATA); 42 } 43 44 u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp) 45 { 46 return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); 47 } 48 49 void hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus) 50 { 51 hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus); 52 /* flush posted write */ 53 hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); 54 } 55 56 void hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask) 57 { 58 hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask); 59 } 60 61 void hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask) 62 { 63 hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask); 64 } 65 66 /* PHY_PWR_CMD */ 67 int hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val) 68 { 69 /* Return if already the state */ 70 if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) == val) 71 return 0; 72 73 /* Command for power control of HDMI PHY */ 74 REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6); 75 76 /* Status of the power control of HDMI PHY */ 77 if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val) 78 != val) { 79 DSSERR("Failed to set PHY power mode to %d\n", val); 80 return -ETIMEDOUT; 81 } 82 83 return 0; 84 } 85 86 /* PLL_PWR_CMD */ 87 int hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val) 88 { 89 /* Command for power control of HDMI PLL */ 90 REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2); 91 92 /* wait till PHY_PWR_STATUS is set */ 93 if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val) 94 != val) { 95 DSSERR("Failed to set PLL_PWR_STATUS\n"); 96 return -ETIMEDOUT; 97 } 98 99 return 0; 100 } 101 102 int hdmi_wp_video_start(struct hdmi_wp_data *wp) 103 { 104 REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31); 105 106 return 0; 107 } 108 109 void hdmi_wp_video_stop(struct hdmi_wp_data *wp) 110 { 111 int i; 112 113 hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE); 114 115 REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31); 116 117 for (i = 0; i < 50; ++i) { 118 u32 v; 119 120 msleep(20); 121 122 v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW); 123 if (v & HDMI_IRQ_VIDEO_FRAME_DONE) 124 return; 125 } 126 127 DSSERR("no HDMI FRAMEDONE when disabling output\n"); 128 } 129 130 void hdmi_wp_video_config_format(struct hdmi_wp_data *wp, 131 const struct hdmi_video_format *video_fmt) 132 { 133 u32 l = 0; 134 135 REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, 136 10, 8); 137 138 l |= FLD_VAL(video_fmt->y_res, 31, 16); 139 l |= FLD_VAL(video_fmt->x_res, 15, 0); 140 hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l); 141 } 142 143 void hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, 144 const struct videomode *vm) 145 { 146 u32 r; 147 bool vsync_inv, hsync_inv; 148 DSSDBG("Enter hdmi_wp_video_config_interface\n"); 149 150 vsync_inv = !!(vm->flags & DISPLAY_FLAGS_VSYNC_LOW); 151 hsync_inv = !!(vm->flags & DISPLAY_FLAGS_HSYNC_LOW); 152 153 r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); 154 r = FLD_MOD(r, 1, 7, 7); /* VSYNC_POL to dispc active high */ 155 r = FLD_MOD(r, 1, 6, 6); /* HSYNC_POL to dispc active high */ 156 r = FLD_MOD(r, vsync_inv, 5, 5); /* CORE_VSYNC_INV */ 157 r = FLD_MOD(r, hsync_inv, 4, 4); /* CORE_HSYNC_INV */ 158 r = FLD_MOD(r, !!(vm->flags & DISPLAY_FLAGS_INTERLACED), 3, 3); 159 r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ 160 hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); 161 } 162 163 void hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, 164 const struct videomode *vm) 165 { 166 u32 timing_h = 0; 167 u32 timing_v = 0; 168 unsigned int hsync_len_offset = 1; 169 170 DSSDBG("Enter hdmi_wp_video_config_timing\n"); 171 172 /* 173 * On OMAP4 and OMAP5 ES1 the HSW field is programmed as is. On OMAP5 174 * ES2+ (including DRA7/AM5 SoCs) HSW field is programmed to hsync_len-1. 175 * However, we don't support OMAP5 ES1 at all, so we can just check for 176 * OMAP4 here. 177 */ 178 if (wp->version == 4) 179 hsync_len_offset = 0; 180 181 timing_h |= FLD_VAL(vm->hback_porch, 31, 20); 182 timing_h |= FLD_VAL(vm->hfront_porch, 19, 8); 183 timing_h |= FLD_VAL(vm->hsync_len - hsync_len_offset, 7, 0); 184 hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h); 185 186 timing_v |= FLD_VAL(vm->vback_porch, 31, 20); 187 timing_v |= FLD_VAL(vm->vfront_porch, 19, 8); 188 timing_v |= FLD_VAL(vm->vsync_len, 7, 0); 189 hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v); 190 } 191 192 void hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, 193 struct videomode *vm, const struct hdmi_config *param) 194 { 195 DSSDBG("Enter hdmi_wp_video_init_format\n"); 196 197 video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; 198 video_fmt->y_res = param->vm.vactive; 199 video_fmt->x_res = param->vm.hactive; 200 201 vm->hback_porch = param->vm.hback_porch; 202 vm->hfront_porch = param->vm.hfront_porch; 203 vm->hsync_len = param->vm.hsync_len; 204 vm->vback_porch = param->vm.vback_porch; 205 vm->vfront_porch = param->vm.vfront_porch; 206 vm->vsync_len = param->vm.vsync_len; 207 208 vm->flags = param->vm.flags; 209 210 if (param->vm.flags & DISPLAY_FLAGS_INTERLACED) { 211 video_fmt->y_res /= 2; 212 vm->vback_porch /= 2; 213 vm->vfront_porch /= 2; 214 vm->vsync_len /= 2; 215 } 216 217 if (param->vm.flags & DISPLAY_FLAGS_DOUBLECLK) { 218 video_fmt->x_res *= 2; 219 vm->hfront_porch *= 2; 220 vm->hsync_len *= 2; 221 vm->hback_porch *= 2; 222 } 223 } 224 225 void hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, 226 struct hdmi_audio_format *aud_fmt) 227 { 228 u32 r; 229 230 DSSDBG("Enter hdmi_wp_audio_config_format\n"); 231 232 r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); 233 if (wp->version == 4) { 234 r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); 235 r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); 236 } 237 r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); 238 r = FLD_MOD(r, aud_fmt->type, 4, 4); 239 r = FLD_MOD(r, aud_fmt->justification, 3, 3); 240 r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); 241 r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); 242 r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); 243 hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r); 244 } 245 246 void hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, 247 struct hdmi_audio_dma *aud_dma) 248 { 249 u32 r; 250 251 DSSDBG("Enter hdmi_wp_audio_config_dma\n"); 252 253 r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2); 254 r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); 255 r = FLD_MOD(r, aud_dma->block_size, 7, 0); 256 hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r); 257 258 r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL); 259 r = FLD_MOD(r, aud_dma->mode, 9, 9); 260 r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); 261 hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r); 262 } 263 264 int hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable) 265 { 266 REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31); 267 268 return 0; 269 } 270 271 int hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable) 272 { 273 REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30); 274 275 return 0; 276 } 277 278 int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp, 279 unsigned int version) 280 { 281 struct resource *res; 282 283 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp"); 284 wp->base = devm_ioremap_resource(&pdev->dev, res); 285 if (IS_ERR(wp->base)) 286 return PTR_ERR(wp->base); 287 288 wp->phys_base = res->start; 289 wp->version = version; 290 291 return 0; 292 } 293 294 phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp) 295 { 296 return wp->phys_base + HDMI_WP_AUDIO_DATA; 297 } 298