Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hdmiphy_drv.c
Go to the documentation of this file.
1 /*
2  * Samsung HDMI Physical interface driver
3  *
4  * Copyright (C) 2010-2011 Samsung Electronics Co.Ltd
5  * Author: Tomasz Stanislawski <[email protected]>
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 <[email protected]>");
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);