162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * HDMI PHY 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/io.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/seq_file.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "omapdss.h" 1662306a36Sopenharmony_ci#include "dss.h" 1762306a36Sopenharmony_ci#include "hdmi.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_civoid hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ 2262306a36Sopenharmony_ci hdmi_read_reg(phy->base, r)) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci DUMPPHY(HDMI_TXPHY_TX_CTRL); 2562306a36Sopenharmony_ci DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); 2662306a36Sopenharmony_ci DUMPPHY(HDMI_TXPHY_POWER_CTRL); 2762306a36Sopenharmony_ci DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); 2862306a36Sopenharmony_ci if (phy->features->bist_ctrl) 2962306a36Sopenharmony_ci DUMPPHY(HDMI_TXPHY_BIST_CONTROL); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ciint hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci int i; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci for (i = 0; i < 8; i += 2) { 3762306a36Sopenharmony_ci u8 lane, pol; 3862306a36Sopenharmony_ci int dx, dy; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci dx = lanes[i]; 4162306a36Sopenharmony_ci dy = lanes[i + 1]; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (dx < 0 || dx >= 8) 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (dy < 0 || dy >= 8) 4762306a36Sopenharmony_ci return -EINVAL; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (dx & 1) { 5062306a36Sopenharmony_ci if (dy != dx - 1) 5162306a36Sopenharmony_ci return -EINVAL; 5262306a36Sopenharmony_ci pol = 1; 5362306a36Sopenharmony_ci } else { 5462306a36Sopenharmony_ci if (dy != dx + 1) 5562306a36Sopenharmony_ci return -EINVAL; 5662306a36Sopenharmony_ci pol = 0; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci lane = dx / 2; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci phy->lane_function[lane] = i / 2; 6262306a36Sopenharmony_ci phy->lane_polarity[lane] = pol; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci static const u16 pad_cfg_list[] = { 7162306a36Sopenharmony_ci 0x0123, 7262306a36Sopenharmony_ci 0x0132, 7362306a36Sopenharmony_ci 0x0312, 7462306a36Sopenharmony_ci 0x0321, 7562306a36Sopenharmony_ci 0x0231, 7662306a36Sopenharmony_ci 0x0213, 7762306a36Sopenharmony_ci 0x1023, 7862306a36Sopenharmony_ci 0x1032, 7962306a36Sopenharmony_ci 0x3012, 8062306a36Sopenharmony_ci 0x3021, 8162306a36Sopenharmony_ci 0x2031, 8262306a36Sopenharmony_ci 0x2013, 8362306a36Sopenharmony_ci 0x1203, 8462306a36Sopenharmony_ci 0x1302, 8562306a36Sopenharmony_ci 0x3102, 8662306a36Sopenharmony_ci 0x3201, 8762306a36Sopenharmony_ci 0x2301, 8862306a36Sopenharmony_ci 0x2103, 8962306a36Sopenharmony_ci 0x1230, 9062306a36Sopenharmony_ci 0x1320, 9162306a36Sopenharmony_ci 0x3120, 9262306a36Sopenharmony_ci 0x3210, 9362306a36Sopenharmony_ci 0x2310, 9462306a36Sopenharmony_ci 0x2130, 9562306a36Sopenharmony_ci }; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci u16 lane_cfg = 0; 9862306a36Sopenharmony_ci int i; 9962306a36Sopenharmony_ci unsigned int lane_cfg_val; 10062306a36Sopenharmony_ci u16 pol_val = 0; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (i = 0; i < 4; ++i) 10362306a36Sopenharmony_ci lane_cfg |= phy->lane_function[i] << ((3 - i) * 4); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci pol_val |= phy->lane_polarity[0] << 0; 10662306a36Sopenharmony_ci pol_val |= phy->lane_polarity[1] << 3; 10762306a36Sopenharmony_ci pol_val |= phy->lane_polarity[2] << 2; 10862306a36Sopenharmony_ci pol_val |= phy->lane_polarity[3] << 1; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i) 11162306a36Sopenharmony_ci if (pad_cfg_list[i] == lane_cfg) 11262306a36Sopenharmony_ci break; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list))) 11562306a36Sopenharmony_ci i = 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci lane_cfg_val = i; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22); 12062306a36Sopenharmony_ci REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ciint hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, 12462306a36Sopenharmony_ci unsigned long lfbitclk) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci u8 freqout; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * Read address 0 in order to get the SCP reset done completed 13062306a36Sopenharmony_ci * Dummy access performed to make sure reset is done 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* 13562306a36Sopenharmony_ci * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the 13662306a36Sopenharmony_ci * HDMI_PHYPWRCMD_LDOON command. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_ci if (phy->features->bist_ctrl) 13962306a36Sopenharmony_ci REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * If the hfbitclk != lfbitclk, it means the lfbitclk was configured 14362306a36Sopenharmony_ci * to be used for TMDS. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_ci if (hfbitclk != lfbitclk) 14662306a36Sopenharmony_ci freqout = 0; 14762306a36Sopenharmony_ci else if (hfbitclk / 10 < phy->features->max_phy) 14862306a36Sopenharmony_ci freqout = 1; 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci freqout = 2; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * Write to phy address 0 to configure the clock 15462306a36Sopenharmony_ci * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_ci REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ 15962306a36Sopenharmony_ci hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Setup max LDO voltage */ 16262306a36Sopenharmony_ci if (phy->features->ldo_voltage) 16362306a36Sopenharmony_ci REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci hdmi_phy_configure_lanes(phy); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic const struct hdmi_phy_features omap44xx_phy_feats = { 17162306a36Sopenharmony_ci .bist_ctrl = false, 17262306a36Sopenharmony_ci .ldo_voltage = true, 17362306a36Sopenharmony_ci .max_phy = 185675000, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct hdmi_phy_features omap54xx_phy_feats = { 17762306a36Sopenharmony_ci .bist_ctrl = true, 17862306a36Sopenharmony_ci .ldo_voltage = false, 17962306a36Sopenharmony_ci .max_phy = 186000000, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ciint hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy, 18362306a36Sopenharmony_ci unsigned int version) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci if (version == 4) 18662306a36Sopenharmony_ci phy->features = &omap44xx_phy_feats; 18762306a36Sopenharmony_ci else 18862306a36Sopenharmony_ci phy->features = &omap54xx_phy_feats; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci phy->base = devm_platform_ioremap_resource_byname(pdev, "phy"); 19162306a36Sopenharmony_ci if (IS_ERR(phy->base)) 19262306a36Sopenharmony_ci return PTR_ERR(phy->base); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return 0; 19562306a36Sopenharmony_ci} 196