hdmi_wp.c (7921B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * HDMI wrapper 4 * 5 * Copyright (C) 2013 Texas Instruments Incorporated - https://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 20void 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 44u32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp) 45{ 46 return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); 47} 48 49void 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 56void 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 61void 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 */ 67int 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 */ 87int 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 102int 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 109void 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 130void 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 143void 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 163void 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 192void 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 225void 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 246void 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 264int 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 271int 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 278int 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 294phys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp) 295{ 296 return wp->phys_base + HDMI_WP_AUDIO_DATA; 297}