162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HDMI wrapper 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define DSS_SUBSYS_NAME "HDMIWP" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/err.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/platform_device.h> 1462306a36Sopenharmony_ci#include <linux/seq_file.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include "omapdss.h" 1762306a36Sopenharmony_ci#include "dss.h" 1862306a36Sopenharmony_ci#include "hdmi.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid hdmi_wp_dump(struct hdmi_wp_data *wp, struct seq_file *s) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, hdmi_read_reg(wp->base, r)) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci DUMPREG(HDMI_WP_REVISION); 2562306a36Sopenharmony_ci DUMPREG(HDMI_WP_SYSCONFIG); 2662306a36Sopenharmony_ci DUMPREG(HDMI_WP_IRQSTATUS_RAW); 2762306a36Sopenharmony_ci DUMPREG(HDMI_WP_IRQSTATUS); 2862306a36Sopenharmony_ci DUMPREG(HDMI_WP_IRQENABLE_SET); 2962306a36Sopenharmony_ci DUMPREG(HDMI_WP_IRQENABLE_CLR); 3062306a36Sopenharmony_ci DUMPREG(HDMI_WP_IRQWAKEEN); 3162306a36Sopenharmony_ci DUMPREG(HDMI_WP_PWR_CTRL); 3262306a36Sopenharmony_ci DUMPREG(HDMI_WP_DEBOUNCE); 3362306a36Sopenharmony_ci DUMPREG(HDMI_WP_VIDEO_CFG); 3462306a36Sopenharmony_ci DUMPREG(HDMI_WP_VIDEO_SIZE); 3562306a36Sopenharmony_ci DUMPREG(HDMI_WP_VIDEO_TIMING_H); 3662306a36Sopenharmony_ci DUMPREG(HDMI_WP_VIDEO_TIMING_V); 3762306a36Sopenharmony_ci DUMPREG(HDMI_WP_CLK); 3862306a36Sopenharmony_ci DUMPREG(HDMI_WP_AUDIO_CFG); 3962306a36Sopenharmony_ci DUMPREG(HDMI_WP_AUDIO_CFG2); 4062306a36Sopenharmony_ci DUMPREG(HDMI_WP_AUDIO_CTRL); 4162306a36Sopenharmony_ci DUMPREG(HDMI_WP_AUDIO_DATA); 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ciu32 hdmi_wp_get_irqstatus(struct hdmi_wp_data *wp) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_civoid hdmi_wp_set_irqstatus(struct hdmi_wp_data *wp, u32 irqstatus) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, irqstatus); 5262306a36Sopenharmony_ci /* flush posted write */ 5362306a36Sopenharmony_ci hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_civoid hdmi_wp_set_irqenable(struct hdmi_wp_data *wp, u32 mask) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_SET, mask); 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid hdmi_wp_clear_irqenable(struct hdmi_wp_data *wp, u32 mask) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_IRQENABLE_CLR, mask); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* PHY_PWR_CMD */ 6762306a36Sopenharmony_ciint hdmi_wp_set_phy_pwr(struct hdmi_wp_data *wp, enum hdmi_phy_pwr val) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci /* Return if already the state */ 7062306a36Sopenharmony_ci if (REG_GET(wp->base, HDMI_WP_PWR_CTRL, 5, 4) == val) 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci /* Command for power control of HDMI PHY */ 7462306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 7, 6); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Status of the power control of HDMI PHY */ 7762306a36Sopenharmony_ci if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 5, 4, val) 7862306a36Sopenharmony_ci != val) { 7962306a36Sopenharmony_ci DSSERR("Failed to set PHY power mode to %d\n", val); 8062306a36Sopenharmony_ci return -ETIMEDOUT; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* PLL_PWR_CMD */ 8762306a36Sopenharmony_ciint hdmi_wp_set_pll_pwr(struct hdmi_wp_data *wp, enum hdmi_pll_pwr val) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci /* Command for power control of HDMI PLL */ 9062306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_PWR_CTRL, val, 3, 2); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* wait till PHY_PWR_STATUS is set */ 9362306a36Sopenharmony_ci if (hdmi_wait_for_bit_change(wp->base, HDMI_WP_PWR_CTRL, 1, 0, val) 9462306a36Sopenharmony_ci != val) { 9562306a36Sopenharmony_ci DSSERR("Failed to set PLL_PWR_STATUS\n"); 9662306a36Sopenharmony_ci return -ETIMEDOUT; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ciint hdmi_wp_video_start(struct hdmi_wp_data *wp) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, true, 31, 31); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci return 0; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_civoid hdmi_wp_video_stop(struct hdmi_wp_data *wp) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci int i; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_IRQSTATUS, HDMI_IRQ_VIDEO_FRAME_DONE); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, false, 31, 31); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci for (i = 0; i < 50; ++i) { 11862306a36Sopenharmony_ci u32 v; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci msleep(20); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci v = hdmi_read_reg(wp->base, HDMI_WP_IRQSTATUS_RAW); 12362306a36Sopenharmony_ci if (v & HDMI_IRQ_VIDEO_FRAME_DONE) 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci DSSERR("no HDMI FRAMEDONE when disabling output\n"); 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_civoid hdmi_wp_video_config_format(struct hdmi_wp_data *wp, 13162306a36Sopenharmony_ci const struct hdmi_video_format *video_fmt) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 l = 0; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_VIDEO_CFG, video_fmt->packing_mode, 13662306a36Sopenharmony_ci 10, 8); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci l |= FLD_VAL(video_fmt->y_res, 31, 16); 13962306a36Sopenharmony_ci l |= FLD_VAL(video_fmt->x_res, 15, 0); 14062306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_VIDEO_SIZE, l); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_civoid hdmi_wp_video_config_interface(struct hdmi_wp_data *wp, 14462306a36Sopenharmony_ci const struct videomode *vm) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci u32 r; 14762306a36Sopenharmony_ci bool vsync_inv, hsync_inv; 14862306a36Sopenharmony_ci DSSDBG("Enter hdmi_wp_video_config_interface\n"); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci vsync_inv = !!(vm->flags & DISPLAY_FLAGS_VSYNC_LOW); 15162306a36Sopenharmony_ci hsync_inv = !!(vm->flags & DISPLAY_FLAGS_HSYNC_LOW); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci r = hdmi_read_reg(wp->base, HDMI_WP_VIDEO_CFG); 15462306a36Sopenharmony_ci r = FLD_MOD(r, 1, 7, 7); /* VSYNC_POL to dispc active high */ 15562306a36Sopenharmony_ci r = FLD_MOD(r, 1, 6, 6); /* HSYNC_POL to dispc active high */ 15662306a36Sopenharmony_ci r = FLD_MOD(r, vsync_inv, 5, 5); /* CORE_VSYNC_INV */ 15762306a36Sopenharmony_ci r = FLD_MOD(r, hsync_inv, 4, 4); /* CORE_HSYNC_INV */ 15862306a36Sopenharmony_ci r = FLD_MOD(r, !!(vm->flags & DISPLAY_FLAGS_INTERLACED), 3, 3); 15962306a36Sopenharmony_ci r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */ 16062306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_VIDEO_CFG, r); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid hdmi_wp_video_config_timing(struct hdmi_wp_data *wp, 16462306a36Sopenharmony_ci const struct videomode *vm) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci u32 timing_h = 0; 16762306a36Sopenharmony_ci u32 timing_v = 0; 16862306a36Sopenharmony_ci unsigned int hsync_len_offset = 1; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci DSSDBG("Enter hdmi_wp_video_config_timing\n"); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* 17362306a36Sopenharmony_ci * On OMAP4 and OMAP5 ES1 the HSW field is programmed as is. On OMAP5 17462306a36Sopenharmony_ci * ES2+ (including DRA7/AM5 SoCs) HSW field is programmed to hsync_len-1. 17562306a36Sopenharmony_ci * However, we don't support OMAP5 ES1 at all, so we can just check for 17662306a36Sopenharmony_ci * OMAP4 here. 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci if (wp->version == 4) 17962306a36Sopenharmony_ci hsync_len_offset = 0; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci timing_h |= FLD_VAL(vm->hback_porch, 31, 20); 18262306a36Sopenharmony_ci timing_h |= FLD_VAL(vm->hfront_porch, 19, 8); 18362306a36Sopenharmony_ci timing_h |= FLD_VAL(vm->hsync_len - hsync_len_offset, 7, 0); 18462306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_H, timing_h); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci timing_v |= FLD_VAL(vm->vback_porch, 31, 20); 18762306a36Sopenharmony_ci timing_v |= FLD_VAL(vm->vfront_porch, 19, 8); 18862306a36Sopenharmony_ci timing_v |= FLD_VAL(vm->vsync_len, 7, 0); 18962306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_VIDEO_TIMING_V, timing_v); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_civoid hdmi_wp_init_vid_fmt_timings(struct hdmi_video_format *video_fmt, 19362306a36Sopenharmony_ci struct videomode *vm, const struct hdmi_config *param) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci DSSDBG("Enter hdmi_wp_video_init_format\n"); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci video_fmt->packing_mode = HDMI_PACK_10b_RGB_YUV444; 19862306a36Sopenharmony_ci video_fmt->y_res = param->vm.vactive; 19962306a36Sopenharmony_ci video_fmt->x_res = param->vm.hactive; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci vm->hback_porch = param->vm.hback_porch; 20262306a36Sopenharmony_ci vm->hfront_porch = param->vm.hfront_porch; 20362306a36Sopenharmony_ci vm->hsync_len = param->vm.hsync_len; 20462306a36Sopenharmony_ci vm->vback_porch = param->vm.vback_porch; 20562306a36Sopenharmony_ci vm->vfront_porch = param->vm.vfront_porch; 20662306a36Sopenharmony_ci vm->vsync_len = param->vm.vsync_len; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci vm->flags = param->vm.flags; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (param->vm.flags & DISPLAY_FLAGS_INTERLACED) { 21162306a36Sopenharmony_ci video_fmt->y_res /= 2; 21262306a36Sopenharmony_ci vm->vback_porch /= 2; 21362306a36Sopenharmony_ci vm->vfront_porch /= 2; 21462306a36Sopenharmony_ci vm->vsync_len /= 2; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (param->vm.flags & DISPLAY_FLAGS_DOUBLECLK) { 21862306a36Sopenharmony_ci video_fmt->x_res *= 2; 21962306a36Sopenharmony_ci vm->hfront_porch *= 2; 22062306a36Sopenharmony_ci vm->hsync_len *= 2; 22162306a36Sopenharmony_ci vm->hback_porch *= 2; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_civoid hdmi_wp_audio_config_format(struct hdmi_wp_data *wp, 22662306a36Sopenharmony_ci struct hdmi_audio_format *aud_fmt) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci u32 r; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci DSSDBG("Enter hdmi_wp_audio_config_format\n"); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG); 23362306a36Sopenharmony_ci if (wp->version == 4) { 23462306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->stereo_channels, 26, 24); 23562306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->active_chnnls_msk, 23, 16); 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->en_sig_blk_strt_end, 5, 5); 23862306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->type, 4, 4); 23962306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->justification, 3, 3); 24062306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->sample_order, 2, 2); 24162306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->samples_per_word, 1, 1); 24262306a36Sopenharmony_ci r = FLD_MOD(r, aud_fmt->sample_size, 0, 0); 24362306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG, r); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_civoid hdmi_wp_audio_config_dma(struct hdmi_wp_data *wp, 24762306a36Sopenharmony_ci struct hdmi_audio_dma *aud_dma) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci u32 r; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci DSSDBG("Enter hdmi_wp_audio_config_dma\n"); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CFG2); 25462306a36Sopenharmony_ci r = FLD_MOD(r, aud_dma->transfer_size, 15, 8); 25562306a36Sopenharmony_ci r = FLD_MOD(r, aud_dma->block_size, 7, 0); 25662306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CFG2, r); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci r = hdmi_read_reg(wp->base, HDMI_WP_AUDIO_CTRL); 25962306a36Sopenharmony_ci r = FLD_MOD(r, aud_dma->mode, 9, 9); 26062306a36Sopenharmony_ci r = FLD_MOD(r, aud_dma->fifo_threshold, 8, 0); 26162306a36Sopenharmony_ci hdmi_write_reg(wp->base, HDMI_WP_AUDIO_CTRL, r); 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ciint hdmi_wp_audio_enable(struct hdmi_wp_data *wp, bool enable) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 31, 31); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci return 0; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciint hdmi_wp_audio_core_req_enable(struct hdmi_wp_data *wp, bool enable) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci REG_FLD_MOD(wp->base, HDMI_WP_AUDIO_CTRL, enable, 30, 30); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ciint hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp, 27962306a36Sopenharmony_ci unsigned int version) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct resource *res; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp"); 28462306a36Sopenharmony_ci wp->base = devm_ioremap_resource(&pdev->dev, res); 28562306a36Sopenharmony_ci if (IS_ERR(wp->base)) 28662306a36Sopenharmony_ci return PTR_ERR(wp->base); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci wp->phys_base = res->start; 28962306a36Sopenharmony_ci wp->version = version; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci return 0; 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ciphys_addr_t hdmi_wp_get_audio_dma_addr(struct hdmi_wp_data *wp) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci return wp->phys_base + HDMI_WP_AUDIO_DATA; 29762306a36Sopenharmony_ci} 298