298 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * HDMI wrapper4 *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 OMAP5174 * 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 for176 * 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}298