blob: 8d13e422de5e57bed2c60f178c3fc5eee5a28c76 [file] [log] [blame]
Archit Taneja5cac5ae2013-10-08 13:07:00 +05301/*
2 * HDMI PHY
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 */
10
11#include <linux/kernel.h>
Archit Taneja5cac5ae2013-10-08 13:07:00 +053012#include <linux/err.h>
13#include <linux/io.h>
14#include <linux/platform_device.h>
15#include <video/omapdss.h>
16
17#include "dss.h"
Archit Tanejaef269582013-09-12 17:45:57 +053018#include "hdmi.h"
Archit Taneja5cac5ae2013-10-08 13:07:00 +053019
Archit Taneja5cac5ae2013-10-08 13:07:00 +053020void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s)
21{
22#define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\
23 hdmi_read_reg(phy->base, r))
24
25 DUMPPHY(HDMI_TXPHY_TX_CTRL);
26 DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL);
27 DUMPPHY(HDMI_TXPHY_POWER_CTRL);
28 DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL);
29}
30
Tomi Valkeinen2f5dc672014-04-17 12:54:02 +030031int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes)
32{
33 int i;
34
35 for (i = 0; i < 8; i += 2) {
36 u8 lane, pol;
37 int dx, dy;
38
39 dx = lanes[i];
40 dy = lanes[i + 1];
41
42 if (dx < 0 || dx >= 8)
43 return -EINVAL;
44
45 if (dy < 0 || dy >= 8)
46 return -EINVAL;
47
48 if (dx & 1) {
49 if (dy != dx - 1)
50 return -EINVAL;
51 pol = 1;
52 } else {
53 if (dy != dx + 1)
54 return -EINVAL;
55 pol = 0;
56 }
57
58 lane = dx / 2;
59
60 phy->lane_function[lane] = i / 2;
61 phy->lane_polarity[lane] = pol;
62 }
63
64 return 0;
65}
66
67static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy)
68{
69 static const u16 pad_cfg_list[] = {
70 0x0123,
71 0x0132,
72 0x0312,
73 0x0321,
74 0x0231,
75 0x0213,
76 0x1023,
77 0x1032,
78 0x3012,
79 0x3021,
80 0x2031,
81 0x2013,
82 0x1203,
83 0x1302,
84 0x3102,
85 0x3201,
86 0x2301,
87 0x2103,
88 0x1230,
89 0x1320,
90 0x3120,
91 0x3210,
92 0x2310,
93 0x2130,
94 };
95
96 u16 lane_cfg = 0;
97 int i;
98 unsigned lane_cfg_val;
99 u16 pol_val = 0;
100
101 for (i = 0; i < 4; ++i)
102 lane_cfg |= phy->lane_function[i] << ((3 - i) * 4);
103
104 pol_val |= phy->lane_polarity[0] << 0;
105 pol_val |= phy->lane_polarity[1] << 3;
106 pol_val |= phy->lane_polarity[2] << 2;
107 pol_val |= phy->lane_polarity[3] << 1;
108
109 for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i)
110 if (pad_cfg_list[i] == lane_cfg)
111 break;
112
113 if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list)))
114 i = 0;
115
116 lane_cfg_val = i;
117
118 REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22);
119 REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27);
120}
121
Tomi Valkeinendcf5f722013-10-28 11:47:34 +0200122int hdmi_phy_configure(struct hdmi_phy_data *phy, struct hdmi_config *cfg)
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530123{
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530124 /*
125 * Read address 0 in order to get the SCP reset done completed
126 * Dummy access performed to make sure reset is done
127 */
128 hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL);
129
130 /*
131 * Write to phy address 0 to configure the clock
132 * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field
133 */
134 REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, 0x1, 31, 30);
135
136 /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */
137 hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000);
138
139 /* Setup max LDO voltage */
140 REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0);
141
Tomi Valkeinen2f5dc672014-04-17 12:54:02 +0300142 hdmi_phy_configure_lanes(phy);
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530143
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530144 return 0;
145}
146
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530147#define PHY_OFFSET 0x300
148#define PHY_SIZE 0x100
149
150int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy)
151{
152 struct resource *res;
153 struct resource temp_res;
154
Tomi Valkeinen77601502013-12-17 14:41:14 +0200155 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy");
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530156 if (!res) {
157 DSSDBG("can't get PHY mem resource by name\n");
158 /*
159 * if hwmod/DT doesn't have the memory resource information
160 * split into HDMI sub blocks by name, we try again by getting
161 * the platform's first resource. this code will be removed when
162 * the driver can get the mem resources by name
163 */
164 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
165 if (!res) {
166 DSSERR("can't get PHY mem resource\n");
167 return -EINVAL;
168 }
169
170 temp_res.start = res->start + PHY_OFFSET;
171 temp_res.end = temp_res.start + PHY_SIZE - 1;
172 res = &temp_res;
173 }
174
175 phy->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
176 if (!phy->base) {
177 DSSERR("can't ioremap TX PHY\n");
178 return -ENOMEM;
179 }
180
Archit Taneja5cac5ae2013-10-08 13:07:00 +0530181 return 0;
182}