[media] move soc_camera i2c drivers into its own dir
[sfrench/cifs-2.6.git] / drivers / media / video / 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);