1
2
3
4
5
6
7
8
9
10
11
12
13#include <linux/module.h>
14#include <linux/i2c.h>
15#include <linux/slab.h>
16#include <linux/clk.h>
17#include <linux/io.h>
18#include <linux/interrupt.h>
19#include <linux/irq.h>
20#include <linux/err.h>
21
22#include <media/v4l2-subdev.h>
23
24MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
25MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
26MODULE_LICENSE("GPL");
27
28struct hdmiphy_conf {
29 unsigned long pixclk;
30 const u8 *data;
31};
32
33struct hdmiphy_ctx {
34 struct v4l2_subdev sd;
35 const struct hdmiphy_conf *conf_tab;
36};
37
38static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = {
39 { .pixclk = 27000000, .data = (u8 [32]) {
40 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
41 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
42 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
43 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
44 },
45 { .pixclk = 27027000, .data = (u8 [32]) {
46 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
47 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87,
48 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
49 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
50 },
51 { .pixclk = 74176000, .data = (u8 [32]) {
52 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
53 0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9,
54 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
55 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
56 },
57 { .pixclk = 74250000, .data = (u8 [32]) {
58 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
59 0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA,
60 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
61 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
62 },
63 { }
64};
65
66static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = {
67 { .pixclk = 27000000, .data = (u8 [32]) {
68 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
69 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
70 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
71 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, }
72 },
73 { .pixclk = 27027000, .data = (u8 [32]) {
74 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
75 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
76 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
77 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, }
78 },
79 { .pixclk = 74176000, .data = (u8 [32]) {
80 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
81 0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
82 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
83 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, }
84 },
85 { .pixclk = 74250000, .data = (u8 [32]) {
86 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
87 0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
88 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
89 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, }
90 },
91 { .pixclk = 148352000, .data = (u8 [32]) {
92 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B,
93 0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9,
94 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
95 0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, }
96 },
97 { .pixclk = 148500000, .data = (u8 [32]) {
98 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40,
99 0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA,
100 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
101 0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, }
102 },
103 { }
104};
105
106static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = {
107 { .pixclk = 27000000, .data = (u8 [32]) {
108 0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08,
109 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
110 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
111 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
112 },
113 { .pixclk = 27027000, .data = (u8 [32]) {
114 0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08,
115 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
116 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71,
117 0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
118 },
119 { .pixclk = 74176000, .data = (u8 [32]) {
120 0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08,
121 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
122 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
123 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
124 },
125 { .pixclk = 74250000, .data = (u8 [32]) {
126 0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08,
127 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
128 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52,
129 0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
130 },
131 { .pixclk = 148500000, .data = (u8 [32]) {
132 0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08,
133 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0,
134 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4,
135 0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
136 },
137 { }
138};
139
140static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = {
141 { .pixclk = 27000000, .data = (u8 [32]) {
142 0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08,
143 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
144 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
145 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
146 },
147 { .pixclk = 27027000, .data = (u8 [32]) {
148 0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08,
149 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
150 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
151 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, }
152 },
153 { .pixclk = 74176000, .data = (u8 [32]) {
154 0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08,
155 0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
156 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
157 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
158 },
159 { .pixclk = 74250000, .data = (u8 [32]) {
160 0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
161 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
162 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
163 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, }
164 },
165 { .pixclk = 148500000, .data = (u8 [32]) {
166 0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
167 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
168 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86,
169 0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, }
170 },
171 { }
172};
173
174static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
175{
176 return container_of(sd, struct hdmiphy_ctx, sd);
177}
178
179static const u8 *hdmiphy_find_conf(unsigned long pixclk,
180 const struct hdmiphy_conf *conf)
181{
182 for (; conf->pixclk; ++conf)
183 if (conf->pixclk == pixclk)
184 return conf->data;
185 return NULL;
186}
187
188static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
189{
190
191 return 0;
192}
193
194static int hdmiphy_s_dv_timings(struct v4l2_subdev *sd,
195 struct v4l2_dv_timings *timings)
196{
197 const u8 *data;
198 u8 buffer[32];
199 int ret;
200 struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
201 struct i2c_client *client = v4l2_get_subdevdata(sd);
202 struct device *dev = &client->dev;
203 unsigned long pixclk = timings->bt.pixelclock;
204
205 dev_info(dev, "s_dv_timings\n");
206 if ((timings->bt.flags & V4L2_DV_FL_REDUCED_FPS) && pixclk == 74250000)
207 pixclk = 74176000;
208 data = hdmiphy_find_conf(pixclk, ctx->conf_tab);
209 if (!data) {
210 dev_err(dev, "format not supported\n");
211 return -EINVAL;
212 }
213
214
215 memcpy(buffer, data, 32);
216 ret = i2c_master_send(client, buffer, 32);
217 if (ret != 32) {
218 dev_err(dev, "failed to configure HDMIPHY via I2C\n");
219 return -EIO;
220 }
221
222 return 0;
223}
224
225static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
226 struct v4l2_dv_timings_cap *cap)
227{
228 cap->type = V4L2_DV_BT_656_1120;
229
230
231 cap->bt.min_pixelclock = 27000000;
232 cap->bt.max_pixelclock = 148500000;
233 return 0;
234}
235
236static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
237{
238 struct i2c_client *client = v4l2_get_subdevdata(sd);
239 struct device *dev = &client->dev;
240 u8 buffer[2];
241 int ret;
242
243 dev_info(dev, "s_stream(%d)\n", enable);
244
245 buffer[0] = 0x1f;
246 buffer[1] = enable ? 0x80 : 0x00;
247
248 ret = i2c_master_send(client, buffer, 2);
249 if (ret != 2) {
250 dev_err(dev, "stream (%d) failed\n", enable);
251 return -EIO;
252 }
253 return 0;
254}
255
256static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
257 .s_power = hdmiphy_s_power,
258};
259
260static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
261 .s_dv_timings = hdmiphy_s_dv_timings,
262 .dv_timings_cap = hdmiphy_dv_timings_cap,
263 .s_stream = hdmiphy_s_stream,
264};
265
266static const struct v4l2_subdev_ops hdmiphy_ops = {
267 .core = &hdmiphy_core_ops,
268 .video = &hdmiphy_video_ops,
269};
270
271static int hdmiphy_probe(struct i2c_client *client,
272 const struct i2c_device_id *id)
273{
274 struct hdmiphy_ctx *ctx;
275
276 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
277 if (!ctx)
278 return -ENOMEM;
279
280 ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data;
281 v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops);
282
283 dev_info(&client->dev, "probe successful\n");
284 return 0;
285}
286
287static int hdmiphy_remove(struct i2c_client *client)
288{
289 struct v4l2_subdev *sd = i2c_get_clientdata(client);
290 struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
291
292 kfree(ctx);
293 dev_info(&client->dev, "remove successful\n");
294
295 return 0;
296}
297
298static const struct i2c_device_id hdmiphy_id[] = {
299 { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 },
300 { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 },
301 { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 },
302 { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 },
303 { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 },
304 { },
305};
306MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
307
308static struct i2c_driver hdmiphy_driver = {
309 .driver = {
310 .name = "s5p-hdmiphy",
311 .owner = THIS_MODULE,
312 },
313 .probe = hdmiphy_probe,
314 .remove = hdmiphy_remove,
315 .id_table = hdmiphy_id,
316};
317
318module_i2c_driver(hdmiphy_driver);
319