Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / 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 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
188 static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
189 {
190         /* to be implemented */
191         return 0;
192 }
193
194 static 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         /* storing configuration to the device */
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
225 static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
226         struct v4l2_dv_timings_cap *cap)
227 {
228         if (cap->pad != 0)
229                 return -EINVAL;
230
231         cap->type = V4L2_DV_BT_656_1120;
232         /* The phy only determines the pixelclock, leave the other values
233          * at 0 to signify that we have no information for them. */
234         cap->bt.min_pixelclock = 27000000;
235         cap->bt.max_pixelclock = 148500000;
236         return 0;
237 }
238
239 static int hdmiphy_s_stream(struct v4l2_subdev *sd, int enable)
240 {
241         struct i2c_client *client = v4l2_get_subdevdata(sd);
242         struct device *dev = &client->dev;
243         u8 buffer[2];
244         int ret;
245
246         dev_info(dev, "s_stream(%d)\n", enable);
247         /* going to/from configuration from/to operation mode */
248         buffer[0] = 0x1f;
249         buffer[1] = enable ? 0x80 : 0x00;
250
251         ret = i2c_master_send(client, buffer, 2);
252         if (ret != 2) {
253                 dev_err(dev, "stream (%d) failed\n", enable);
254                 return -EIO;
255         }
256         return 0;
257 }
258
259 static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
260         .s_power =  hdmiphy_s_power,
261 };
262
263 static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
264         .s_dv_timings = hdmiphy_s_dv_timings,
265         .s_stream =  hdmiphy_s_stream,
266 };
267
268 static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = {
269         .dv_timings_cap = hdmiphy_dv_timings_cap,
270 };
271
272 static const struct v4l2_subdev_ops hdmiphy_ops = {
273         .core = &hdmiphy_core_ops,
274         .video = &hdmiphy_video_ops,
275         .pad = &hdmiphy_pad_ops,
276 };
277
278 static int hdmiphy_probe(struct i2c_client *client,
279                          const struct i2c_device_id *id)
280 {
281         struct hdmiphy_ctx *ctx;
282
283         ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
284         if (!ctx)
285                 return -ENOMEM;
286
287         ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data;
288         v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops);
289
290         dev_info(&client->dev, "probe successful\n");
291         return 0;
292 }
293
294 static int hdmiphy_remove(struct i2c_client *client)
295 {
296         struct v4l2_subdev *sd = i2c_get_clientdata(client);
297         struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
298
299         kfree(ctx);
300         dev_info(&client->dev, "remove successful\n");
301
302         return 0;
303 }
304
305 static const struct i2c_device_id hdmiphy_id[] = {
306         { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 },
307         { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 },
308         { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 },
309         { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 },
310         { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 },
311         { },
312 };
313 MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
314
315 static struct i2c_driver hdmiphy_driver = {
316         .driver = {
317                 .name   = "s5p-hdmiphy",
318                 .owner  = THIS_MODULE,
319         },
320         .probe          = hdmiphy_probe,
321         .remove         = hdmiphy_remove,
322         .id_table = hdmiphy_id,
323 };
324
325 module_i2c_driver(hdmiphy_driver);