Merge tag 'stable/for-linus-3.7-rc0-tag' of git://git.kernel.org/pub/scm/linux/kernel...
[deliverable/linux.git] / drivers / media / platform / s5p-tv / hdmiphy_drv.c
1 /*
2 * Samsung HDMI Physical interface driver
3 *
4 * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
5 * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version.
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
24 MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
25 MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
26 MODULE_LICENSE("GPL");
27
28 struct hdmiphy_conf {
29 unsigned long pixclk;
30 const u8 *data;
31 };
32
33 struct hdmiphy_ctx {
34 struct v4l2_subdev sd;
35 const struct hdmiphy_conf *conf_tab;
36 };
37
38 static 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 { /* end marker */ }
64 };
65
66 static 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 { /* end marker */ }
104 };
105
106 static 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 { /* end marker */ }
138 };
139
140 static 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 { /* end marker */ }
172 };
173
174 static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
175 {
176 return container_of(sd, struct hdmiphy_ctx, sd);
177 }
178
179 static unsigned long hdmiphy_preset_to_pixclk(u32 preset)
180 {
181 static const unsigned long pixclk[] = {
182 [V4L2_DV_480P59_94] = 27000000,
183 [V4L2_DV_576P50] = 27000000,
184 [V4L2_DV_720P59_94] = 74176000,
185 [V4L2_DV_720P50] = 74250000,
186 [V4L2_DV_720P60] = 74250000,
187 [V4L2_DV_1080P24] = 74250000,
188 [V4L2_DV_1080P30] = 74250000,
189 [V4L2_DV_1080I50] = 74250000,
190 [V4L2_DV_1080I60] = 74250000,
191 [V4L2_DV_1080P50] = 148500000,
192 [V4L2_DV_1080P60] = 148500000,
193 };
194 if (preset < ARRAY_SIZE(pixclk))
195 return pixclk[preset];
196 else
197 return 0;
198 }
199
200 static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf)
201 {
202 unsigned long pixclk;
203
204 pixclk = hdmiphy_preset_to_pixclk(preset);
205 if (!pixclk)
206 return NULL;
207
208 for (; conf->pixclk; ++conf)
209 if (conf->pixclk == pixclk)
210 return conf->data;
211 return NULL;
212 }
213
214 static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
215 {
216 /* to be implemented */
217 return 0;
218 }
219
220 static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd,
221 struct v4l2_dv_preset *preset)
222 {
223 const u8 *data;
224 u8 buffer[32];
225 int ret;
226 struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
227 struct i2c_client *client = v4l2_get_subdevdata(sd);
228 struct device *dev = &client->dev;
229
230 dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset);
231 data = hdmiphy_find_conf(preset->preset, ctx->conf_tab);
232 if (!data) {
233 dev_err(dev, "format not supported\n");
234 return -EINVAL;
235 }
236
237 /* storing configuration to the device */
238 memcpy(buffer, data, 32);
239 ret = i2c_master_send(client, buffer, 32);
240 if (ret != 32) {
241 dev_err(dev, "failed to configure HDMIPHY via I2C\n");
242 return -EIO;
243 }
244
245 return 0;
246 }
247
248 static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
249 {
250 struct i2c_client *client = v4l2_get_subdevdata(sd);
251 struct device *dev = &client->dev;
252 u8 buffer[2];
253 int ret;
254
255 dev_info(dev, "s_stream(%d)\n", enable);
256 /* going to/from configuration from/to operation mode */
257 buffer[0] = 0x1f;
258 buffer[1] = enable ? 0x80 : 0x00;
259
260 ret = i2c_master_send(client, buffer, 2);
261 if (ret != 2) {
262 dev_err(dev, "stream (%d) failed\n", enable);
263 return -EIO;
264 }
265 return 0;
266 }
267
268 static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
269 .s_power = hdmiphy_s_power,
270 };
271
272 static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
273 .s_dv_preset = hdmiphy_s_dv_preset,
274 .s_stream = hdmiphy_s_stream,
275 };
276
277 static const struct v4l2_subdev_ops hdmiphy_ops = {
278 .core = &hdmiphy_core_ops,
279 .video = &hdmiphy_video_ops,
280 };
281
282 static int __devinit hdmiphy_probe(struct i2c_client *client,
283 const struct i2c_device_id *id)
284 {
285 struct hdmiphy_ctx *ctx;
286
287 ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
288 if (!ctx)
289 return -ENOMEM;
290
291 ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data;
292 v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops);
293
294 dev_info(&client->dev, "probe successful\n");
295 return 0;
296 }
297
298 static int __devexit hdmiphy_remove(struct i2c_client *client)
299 {
300 struct v4l2_subdev *sd = i2c_get_clientdata(client);
301 struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
302
303 kfree(ctx);
304 dev_info(&client->dev, "remove successful\n");
305
306 return 0;
307 }
308
309 static const struct i2c_device_id hdmiphy_id[] = {
310 { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 },
311 { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 },
312 { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 },
313 { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 },
314 { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 },
315 { },
316 };
317 MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
318
319 static struct i2c_driver hdmiphy_driver = {
320 .driver = {
321 .name = "s5p-hdmiphy",
322 .owner = THIS_MODULE,
323 },
324 .probe = hdmiphy_probe,
325 .remove = __devexit_p(hdmiphy_remove),
326 .id_table = hdmiphy_id,
327 };
328
329 module_i2c_driver(hdmiphy_driver);
This page took 0.075803 seconds and 6 git commands to generate.