dt-bindings: mailbox: Add Amlogic Meson MHU Bindings
[deliverable/linux.git] / drivers / media / platform / s5p-tv / hdmiphy_drv.c
CommitLineData
a52074ee
TS
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
24MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
25MODULE_DESCRIPTION("Samsung HDMI Physical interface driver");
26MODULE_LICENSE("GPL");
27
28struct hdmiphy_conf {
2470ea3f 29 unsigned long pixclk;
a52074ee
TS
30 const u8 *data;
31};
32
2470ea3f
TS
33struct hdmiphy_ctx {
34 struct v4l2_subdev sd;
35 const struct hdmiphy_conf *conf_tab;
a52074ee
TS
36};
37
2470ea3f
TS
38static 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 */ }
a52074ee
TS
64};
65
2470ea3f
TS
66static 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 */ }
a52074ee
TS
104};
105
2470ea3f
TS
106static 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 */ }
a52074ee
TS
138};
139
2470ea3f
TS
140static 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 */ }
a52074ee
TS
172};
173
2470ea3f
TS
174static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd)
175{
176 return container_of(sd, struct hdmiphy_ctx, sd);
177}
178
8cf2f7ad
HV
179static const u8 *hdmiphy_find_conf(unsigned long pixclk,
180 const struct hdmiphy_conf *conf)
a52074ee 181{
2470ea3f
TS
182 for (; conf->pixclk; ++conf)
183 if (conf->pixclk == pixclk)
184 return conf->data;
a52074ee
TS
185 return NULL;
186}
187
188static int hdmiphy_s_power(struct v4l2_subdev *sd, int on)
189{
190 /* to be implemented */
191 return 0;
192}
193
8cf2f7ad
HV
194static 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);
a52074ee
TS
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
8cf2f7ad
HV
225static int hdmiphy_dv_timings_cap(struct v4l2_subdev *sd,
226 struct v4l2_dv_timings_cap *cap)
227{
1579a9d3
LP
228 if (cap->pad != 0)
229 return -EINVAL;
230
8cf2f7ad
HV
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
a52074ee
TS
239static 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
259static const struct v4l2_subdev_core_ops hdmiphy_core_ops = {
260 .s_power = hdmiphy_s_power,
261};
262
263static const struct v4l2_subdev_video_ops hdmiphy_video_ops = {
8cf2f7ad 264 .s_dv_timings = hdmiphy_s_dv_timings,
a52074ee
TS
265 .s_stream = hdmiphy_s_stream,
266};
267
1579a9d3
LP
268static const struct v4l2_subdev_pad_ops hdmiphy_pad_ops = {
269 .dv_timings_cap = hdmiphy_dv_timings_cap,
270};
271
a52074ee
TS
272static const struct v4l2_subdev_ops hdmiphy_ops = {
273 .core = &hdmiphy_core_ops,
274 .video = &hdmiphy_video_ops,
1579a9d3 275 .pad = &hdmiphy_pad_ops,
a52074ee
TS
276};
277
4c62e976
GKH
278static int hdmiphy_probe(struct i2c_client *client,
279 const struct i2c_device_id *id)
a52074ee 280{
2470ea3f
TS
281 struct hdmiphy_ctx *ctx;
282
45b56d57 283 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
2470ea3f
TS
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);
a52074ee 289
a52074ee
TS
290 dev_info(&client->dev, "probe successful\n");
291 return 0;
292}
293
4c62e976 294static int hdmiphy_remove(struct i2c_client *client)
a52074ee 295{
2470ea3f
TS
296 struct v4l2_subdev *sd = i2c_get_clientdata(client);
297 struct hdmiphy_ctx *ctx = sd_to_ctx(sd);
298
299 kfree(ctx);
a52074ee 300 dev_info(&client->dev, "remove successful\n");
2470ea3f 301
a52074ee
TS
302 return 0;
303}
304
305static const struct i2c_device_id hdmiphy_id[] = {
2470ea3f
TS
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 },
a52074ee
TS
311 { },
312};
313MODULE_DEVICE_TABLE(i2c, hdmiphy_id);
314
315static struct i2c_driver hdmiphy_driver = {
316 .driver = {
317 .name = "s5p-hdmiphy",
a52074ee
TS
318 },
319 .probe = hdmiphy_probe,
4c62e976 320 .remove = hdmiphy_remove,
a52074ee
TS
321 .id_table = hdmiphy_id,
322};
323
c6e8d86f 324module_i2c_driver(hdmiphy_driver);
This page took 0.344424 seconds and 5 git commands to generate.