Merge branch 'slab/next' of git://git.kernel.org/pub/scm/linux/kernel/git/penberg...
[cascardo/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 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         cap->type = V4L2_DV_BT_656_1120;
229         /* The phy only determines the pixelclock, leave the other values
230          * at 0 to signify that we have no information for them. */
231         cap->bt.min_pixelclock = 27000000;
232         cap->bt.max_pixelclock = 148500000;
233         return 0;
234 }
235
236 static 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         /* going to/from configuration from/to operation mode */
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
256 static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
257         .s_power =  hdmiphy_s_power,
258 };
259
260 static 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
266 static const struct v4l2_subdev_ops hdmiphy_ops = {
267         .core = &hdmiphy_core_ops,
268         .video = &hdmiphy_video_ops,
269 };
270
271 static 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
287 static 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
298 static 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 };
306 MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
307
308 static 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
318 module_i2c_driver(hdmiphy_driver);