aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ps3/ps3av.c
diff options
context:
space:
mode:
authorGeert Uytterhoeven <Geert.Uytterhoeven@sonycom.com>2007-02-12 03:55:16 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-02-12 12:48:44 -0500
commit11227fd1922dc5dda691586852cfd220dd383f37 (patch)
treee6631b1e5be83876bbe2c6e5cea3a427ce61a3fa /drivers/ps3/ps3av.c
parent5b8e8ee6c65a34d8aafaeb8e2eaa97e496c2567c (diff)
[PATCH] ps3: AV Settings Driver
Add the PS3 AV Settings Driver. The AV Settings driver is used to control Audio and Video settings. It communicates with the policy manager through the virtual uart. Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Cc: James Simmons <jsimmons@infradead.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Paul Mackerras <paulus@samba.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/ps3/ps3av.c')
-rw-r--r--drivers/ps3/ps3av.c974
1 files changed, 974 insertions, 0 deletions
diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c
new file mode 100644
index 000000000000..1926b4d3e1f4
--- /dev/null
+++ b/drivers/ps3/ps3av.c
@@ -0,0 +1,974 @@
1/*
2 * Copyright (C) 2006 Sony Computer Entertainment Inc.
3 * Copyright 2006, 2007 Sony Corporation
4 *
5 * AV backend support for PS3
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
9 * by the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#include <linux/module.h>
22#include <linux/delay.h>
23#include <linux/notifier.h>
24#include <linux/reboot.h>
25#include <linux/kernel.h>
26#include <linux/ioctl.h>
27#include <asm/lv1call.h>
28#include <asm/ps3av.h>
29#include <asm/ps3.h>
30
31#include "vuart.h"
32
33#define BUFSIZE 4096 /* vuart buf size */
34#define PS3AV_BUF_SIZE 512 /* max packet size */
35
36static int timeout = 5000; /* in msec ( 5 sec ) */
37module_param(timeout, int, 0644);
38
39static struct ps3av ps3av;
40
41static struct ps3_vuart_port_device ps3av_dev = {
42 .match_id = PS3_MATCH_ID_AV_SETTINGS
43};
44
45/* color space */
46#define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8
47#define RGB8 PS3AV_CMD_VIDEO_CS_RGB_8
48/* format */
49#define XRGB PS3AV_CMD_VIDEO_FMT_X8R8G8B8
50/* aspect */
51#define A_N PS3AV_CMD_AV_ASPECT_4_3
52#define A_W PS3AV_CMD_AV_ASPECT_16_9
53static const struct avset_video_mode {
54 u32 cs;
55 u32 fmt;
56 u32 vid;
57 u32 aspect;
58 u32 x;
59 u32 y;
60 u32 interlace;
61 u32 freq;
62} video_mode_table[] = {
63 { 0, }, /* auto */
64 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480, 1, 60},
65 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480, 0, 60},
66 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_N, 1280, 720, 0, 60},
67 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080, 1, 60},
68 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080, 0, 60},
69 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576, 1, 50},
70 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576, 0, 50},
71 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_N, 1280, 720, 0, 50},
72 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080, 1, 50},
73 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080, 0, 50},
74 { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768, 0, 60},
75 { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024, 0, 60},
76 { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200, 0, 60},
77};
78
79/* supported CIDs */
80static u32 cmd_table[] = {
81 /* init */
82 PS3AV_CID_AV_INIT,
83 PS3AV_CID_AV_FIN,
84 PS3AV_CID_VIDEO_INIT,
85 PS3AV_CID_AUDIO_INIT,
86
87 /* set */
88 PS3AV_CID_AV_ENABLE_EVENT,
89 PS3AV_CID_AV_DISABLE_EVENT,
90
91 PS3AV_CID_AV_VIDEO_CS,
92 PS3AV_CID_AV_VIDEO_MUTE,
93 PS3AV_CID_AV_VIDEO_DISABLE_SIG,
94 PS3AV_CID_AV_AUDIO_PARAM,
95 PS3AV_CID_AV_AUDIO_MUTE,
96 PS3AV_CID_AV_HDMI_MODE,
97 PS3AV_CID_AV_TV_MUTE,
98
99 PS3AV_CID_VIDEO_MODE,
100 PS3AV_CID_VIDEO_FORMAT,
101 PS3AV_CID_VIDEO_PITCH,
102
103 PS3AV_CID_AUDIO_MODE,
104 PS3AV_CID_AUDIO_MUTE,
105 PS3AV_CID_AUDIO_ACTIVE,
106 PS3AV_CID_AUDIO_INACTIVE,
107 PS3AV_CID_AVB_PARAM,
108
109 /* get */
110 PS3AV_CID_AV_GET_HW_CONF,
111 PS3AV_CID_AV_GET_MONITOR_INFO,
112
113 /* event */
114 PS3AV_CID_EVENT_UNPLUGGED,
115 PS3AV_CID_EVENT_PLUGGED,
116 PS3AV_CID_EVENT_HDCP_DONE,
117 PS3AV_CID_EVENT_HDCP_FAIL,
118 PS3AV_CID_EVENT_HDCP_AUTH,
119 PS3AV_CID_EVENT_HDCP_ERROR,
120
121 0
122};
123
124#define PS3AV_EVENT_CMD_MASK 0x10000000
125#define PS3AV_EVENT_ID_MASK 0x0000ffff
126#define PS3AV_CID_MASK 0xffffffff
127#define PS3AV_REPLY_BIT 0x80000000
128
129#define ps3av_event_get_port_id(cid) ((cid >> 16) & 0xff)
130
131static u32 *ps3av_search_cmd_table(u32 cid, u32 mask)
132{
133 u32 *table;
134 int i;
135
136 table = cmd_table;
137 for (i = 0;; table++, i++) {
138 if ((*table & mask) == (cid & mask))
139 break;
140 if (*table == 0)
141 return NULL;
142 }
143 return table;
144}
145
146static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr)
147{
148 u32 *table;
149
150 if (hdr->cid & PS3AV_EVENT_CMD_MASK) {
151 table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK);
152 if (table)
153 dev_dbg(&ps3av_dev.core,
154 "recv event packet cid:%08x port:0x%x size:%d\n",
155 hdr->cid, ps3av_event_get_port_id(hdr->cid),
156 hdr->size);
157 else
158 printk(KERN_ERR
159 "%s: failed event packet, cid:%08x size:%d\n",
160 __FUNCTION__, hdr->cid, hdr->size);
161 return 1; /* receive event packet */
162 }
163 return 0;
164}
165
166static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,
167 struct ps3av_reply_hdr *recv_buf, int write_len,
168 int read_len)
169{
170 int res;
171 u32 cmd;
172 int event;
173
174 if (!ps3av.available)
175 return -ENODEV;
176
177 /* send pkt */
178 res = ps3av_vuart_write(ps3av.dev, send_buf, write_len);
179 if (res < 0) {
180 dev_dbg(&ps3av_dev.core,
181 "%s: ps3av_vuart_write() failed (result=%d)\n",
182 __FUNCTION__, res);
183 return res;
184 }
185
186 /* recv pkt */
187 cmd = send_buf->cid;
188 do {
189 /* read header */
190 res = ps3av_vuart_read(ps3av.dev, recv_buf, PS3AV_HDR_SIZE,
191 timeout);
192 if (res != PS3AV_HDR_SIZE) {
193 dev_dbg(&ps3av_dev.core,
194 "%s: ps3av_vuart_read() failed (result=%d)\n",
195 __FUNCTION__, res);
196 return res;
197 }
198
199 /* read body */
200 res = ps3av_vuart_read(ps3av.dev, &recv_buf->cid,
201 recv_buf->size, timeout);
202 if (res < 0) {
203 dev_dbg(&ps3av_dev.core,
204 "%s: ps3av_vuart_read() failed (result=%d)\n",
205 __FUNCTION__, res);
206 return res;
207 }
208 res += PS3AV_HDR_SIZE; /* total len */
209 event = ps3av_parse_event_packet(recv_buf);
210 /* ret > 0 event packet */
211 } while (event);
212
213 if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) {
214 dev_dbg(&ps3av_dev.core, "%s: reply err (result=%x)\n",
215 __FUNCTION__, recv_buf->cid);
216 return -EINVAL;
217 }
218
219 return 0;
220}
221
222static int ps3av_process_reply_packet(struct ps3av_send_hdr *cmd_buf,
223 const struct ps3av_reply_hdr *recv_buf,
224 int user_buf_size)
225{
226 int return_len;
227
228 if (recv_buf->version != PS3AV_VERSION) {
229 dev_dbg(&ps3av_dev.core, "reply_packet invalid version:%x\n",
230 recv_buf->version);
231 return -EFAULT;
232 }
233 return_len = recv_buf->size + PS3AV_HDR_SIZE;
234 if (return_len > user_buf_size)
235 return_len = user_buf_size;
236 memcpy(cmd_buf, recv_buf, return_len);
237 return 0; /* success */
238}
239
240void ps3av_set_hdr(u32 cid, u16 size, struct ps3av_send_hdr *hdr)
241{
242 hdr->version = PS3AV_VERSION;
243 hdr->size = size - PS3AV_HDR_SIZE;
244 hdr->cid = cid;
245}
246
247int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size,
248 struct ps3av_send_hdr *buf)
249{
250 int res = 0;
251 union {
252 struct ps3av_reply_hdr reply_hdr;
253 u8 raw[PS3AV_BUF_SIZE];
254 } recv_buf;
255
256 u32 *table;
257
258 BUG_ON(!ps3av.available);
259
260 if (down_interruptible(&ps3av.sem))
261 return -ERESTARTSYS;
262
263 table = ps3av_search_cmd_table(cid, PS3AV_CID_MASK);
264 BUG_ON(!table);
265 BUG_ON(send_len < PS3AV_HDR_SIZE);
266 BUG_ON(usr_buf_size < send_len);
267 BUG_ON(usr_buf_size > PS3AV_BUF_SIZE);
268
269 /* create header */
270 ps3av_set_hdr(cid, send_len, buf);
271
272 /* send packet via vuart */
273 res = ps3av_send_cmd_pkt(buf, &recv_buf.reply_hdr, send_len,
274 usr_buf_size);
275 if (res < 0) {
276 printk(KERN_ERR
277 "%s: ps3av_send_cmd_pkt() failed (result=%d)\n",
278 __FUNCTION__, res);
279 goto err;
280 }
281
282 /* process reply packet */
283 res = ps3av_process_reply_packet(buf, &recv_buf.reply_hdr,
284 usr_buf_size);
285 if (res < 0) {
286 printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n",
287 __FUNCTION__, res);
288 goto err;
289 }
290
291 up(&ps3av.sem);
292 return 0;
293
294 err:
295 up(&ps3av.sem);
296 printk(KERN_ERR "%s: failed cid:%x res:%d\n", __FUNCTION__, cid, res);
297 return res;
298}
299
300static int ps3av_set_av_video_mute(u32 mute)
301{
302 int i, num_of_av_port, res;
303
304 num_of_av_port = ps3av.av_hw_conf.num_of_hdmi +
305 ps3av.av_hw_conf.num_of_avmulti;
306 /* video mute on */
307 for (i = 0; i < num_of_av_port; i++) {
308 res = ps3av_cmd_av_video_mute(1, &ps3av.av_port[i], mute);
309 if (res < 0)
310 return -1;
311 }
312
313 return 0;
314}
315
316static int ps3av_set_video_disable_sig(void)
317{
318 int i, num_of_hdmi_port, num_of_av_port, res;
319
320 num_of_hdmi_port = ps3av.av_hw_conf.num_of_hdmi;
321 num_of_av_port = ps3av.av_hw_conf.num_of_hdmi +
322 ps3av.av_hw_conf.num_of_avmulti;
323
324 /* tv mute */
325 for (i = 0; i < num_of_hdmi_port; i++) {
326 res = ps3av_cmd_av_tv_mute(ps3av.av_port[i],
327 PS3AV_CMD_MUTE_ON);
328 if (res < 0)
329 return -1;
330 }
331 msleep(100);
332
333 /* video mute on */
334 for (i = 0; i < num_of_av_port; i++) {
335 res = ps3av_cmd_av_video_disable_sig(ps3av.av_port[i]);
336 if (res < 0)
337 return -1;
338 if (i < num_of_hdmi_port) {
339 res = ps3av_cmd_av_tv_mute(ps3av.av_port[i],
340 PS3AV_CMD_MUTE_OFF);
341 if (res < 0)
342 return -1;
343 }
344 }
345 msleep(300);
346
347 return 0;
348}
349
350static int ps3av_set_audio_mute(u32 mute)
351{
352 int i, num_of_av_port, num_of_opt_port, res;
353
354 num_of_av_port = ps3av.av_hw_conf.num_of_hdmi +
355 ps3av.av_hw_conf.num_of_avmulti;
356 num_of_opt_port = ps3av.av_hw_conf.num_of_spdif;
357
358 for (i = 0; i < num_of_av_port; i++) {
359 res = ps3av_cmd_av_audio_mute(1, &ps3av.av_port[i], mute);
360 if (res < 0)
361 return -1;
362 }
363 for (i = 0; i < num_of_opt_port; i++) {
364 res = ps3av_cmd_audio_mute(1, &ps3av.opt_port[i], mute);
365 if (res < 0)
366 return -1;
367 }
368
369 return 0;
370}
371
372int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source)
373{
374 struct ps3av_pkt_avb_param avb_param;
375 int i, num_of_audio, vid, res;
376 struct ps3av_pkt_audio_mode audio_mode;
377 u32 len = 0;
378
379 num_of_audio = ps3av.av_hw_conf.num_of_hdmi +
380 ps3av.av_hw_conf.num_of_avmulti +
381 ps3av.av_hw_conf.num_of_spdif;
382
383 avb_param.num_of_video_pkt = 0;
384 avb_param.num_of_audio_pkt = PS3AV_AVB_NUM_AUDIO; /* always 0 */
385 avb_param.num_of_av_video_pkt = 0;
386 avb_param.num_of_av_audio_pkt = ps3av.av_hw_conf.num_of_hdmi;
387
388 vid = video_mode_table[ps3av.ps3av_mode].vid;
389
390 /* audio mute */
391 ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON);
392
393 /* audio inactive */
394 res = ps3av_cmd_audio_active(0, ps3av.audio_port);
395 if (res < 0)
396 dev_dbg(&ps3av_dev.core,
397 "ps3av_cmd_audio_active OFF failed\n");
398
399 /* audio_pkt */
400 for (i = 0; i < num_of_audio; i++) {
401 ps3av_cmd_set_audio_mode(&audio_mode, ps3av.av_port[i], ch, fs,
402 word_bits, format, source);
403 if (i < ps3av.av_hw_conf.num_of_hdmi) {
404 /* hdmi only */
405 len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len],
406 ps3av.av_port[i],
407 &audio_mode, vid);
408 }
409 /* audio_mode pkt should be sent separately */
410 res = ps3av_cmd_audio_mode(&audio_mode);
411 if (res < 0)
412 dev_dbg(&ps3av_dev.core,
413 "ps3av_cmd_audio_mode failed, port:%x\n", i);
414 }
415
416 /* send command using avb pkt */
417 len += offsetof(struct ps3av_pkt_avb_param, buf);
418 res = ps3av_cmd_avb_param(&avb_param, len);
419 if (res < 0)
420 dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n");
421
422 /* audio mute */
423 ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF);
424
425 /* audio active */
426 res = ps3av_cmd_audio_active(1, ps3av.audio_port);
427 if (res < 0)
428 dev_dbg(&ps3av_dev.core, "ps3av_cmd_audio_active ON failed\n");
429
430 return 0;
431}
432
433EXPORT_SYMBOL_GPL(ps3av_set_audio_mode);
434
435static int ps3av_set_videomode(void)
436{
437 /* av video mute */
438 ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON);
439
440 /* wake up ps3avd to do the actual video mode setting */
441 up(&ps3av.ping);
442
443 return 0;
444}
445
446static void ps3av_set_videomode_cont(u32 id, u32 old_id)
447{
448 struct ps3av_pkt_avb_param avb_param;
449 int i;
450 u32 len = 0, av_video_cs;
451 const struct avset_video_mode *video_mode;
452 int res;
453
454 video_mode = &video_mode_table[id & PS3AV_MODE_MASK];
455
456 avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */
457 avb_param.num_of_audio_pkt = 0;
458 avb_param.num_of_av_video_pkt = ps3av.av_hw_conf.num_of_hdmi +
459 ps3av.av_hw_conf.num_of_avmulti;
460 avb_param.num_of_av_audio_pkt = 0;
461
462 /* video signal off */
463 ps3av_set_video_disable_sig();
464
465 /* Retail PS3 product doesn't support this */
466 if (id & PS3AV_MODE_HDCP_OFF) {
467 res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF);
468 if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE)
469 dev_dbg(&ps3av_dev.core, "Not supported\n");
470 else if (res)
471 dev_dbg(&ps3av_dev.core,
472 "ps3av_cmd_av_hdmi_mode failed\n");
473 } else if (old_id & PS3AV_MODE_HDCP_OFF) {
474 res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL);
475 if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE)
476 dev_dbg(&ps3av_dev.core,
477 "ps3av_cmd_av_hdmi_mode failed\n");
478 }
479
480 /* video_pkt */
481 for (i = 0; i < avb_param.num_of_video_pkt; i++)
482 len += ps3av_cmd_set_video_mode(&avb_param.buf[len],
483 ps3av.head[i], video_mode->vid,
484 video_mode->fmt, id);
485 /* av_video_pkt */
486 for (i = 0; i < avb_param.num_of_av_video_pkt; i++) {
487 if (id & PS3AV_MODE_DVI || id & PS3AV_MODE_RGB)
488 av_video_cs = RGB8;
489 else
490 av_video_cs = video_mode->cs;
491#ifndef PS3AV_HDMI_YUV
492 if (ps3av.av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 ||
493 ps3av.av_port[i] == PS3AV_CMD_AVPORT_HDMI_1)
494 av_video_cs = RGB8; /* use RGB for HDMI */
495#endif
496 len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len],
497 ps3av.av_port[i],
498 video_mode->vid, av_video_cs,
499 video_mode->aspect, id);
500 }
501 /* send command using avb pkt */
502 len += offsetof(struct ps3av_pkt_avb_param, buf);
503 res = ps3av_cmd_avb_param(&avb_param, len);
504 if (res == PS3AV_STATUS_NO_SYNC_HEAD)
505 printk(KERN_WARNING
506 "%s: Command failed. Please try your request again. \n",
507 __FUNCTION__);
508 else if (res)
509 dev_dbg(&ps3av_dev.core, "ps3av_cmd_avb_param failed\n");
510
511 msleep(1500);
512 /* av video mute */
513 ps3av_set_av_video_mute(PS3AV_CMD_MUTE_OFF);
514}
515
516static int ps3avd(void *p)
517{
518 struct ps3av *info = p;
519
520 daemonize("ps3avd");
521 while (1) {
522 down(&info->ping);
523 ps3av_set_videomode_cont(info->ps3av_mode,
524 info->ps3av_mode_old);
525 up(&info->pong);
526 }
527 return 0;
528}
529
530static int ps3av_vid2table_id(int vid)
531{
532 int i;
533
534 for (i = 1; i < ARRAY_SIZE(video_mode_table); i++)
535 if (video_mode_table[i].vid == vid)
536 return i;
537 return -1;
538}
539
540static int ps3av_resbit2vid(u32 res_50, u32 res_60)
541{
542 int vid = -1;
543
544 if (res_50 > res_60) { /* if res_50 == res_60, res_60 will be used */
545 if (res_50 & PS3AV_RESBIT_1920x1080P)
546 vid = PS3AV_CMD_VIDEO_VID_1080P_50HZ;
547 else if (res_50 & PS3AV_RESBIT_1920x1080I)
548 vid = PS3AV_CMD_VIDEO_VID_1080I_50HZ;
549 else if (res_50 & PS3AV_RESBIT_1280x720P)
550 vid = PS3AV_CMD_VIDEO_VID_720P_50HZ;
551 else if (res_50 & PS3AV_RESBIT_720x576P)
552 vid = PS3AV_CMD_VIDEO_VID_576P;
553 else
554 vid = -1;
555 } else {
556 if (res_60 & PS3AV_RESBIT_1920x1080P)
557 vid = PS3AV_CMD_VIDEO_VID_1080P_60HZ;
558 else if (res_60 & PS3AV_RESBIT_1920x1080I)
559 vid = PS3AV_CMD_VIDEO_VID_1080I_60HZ;
560 else if (res_60 & PS3AV_RESBIT_1280x720P)
561 vid = PS3AV_CMD_VIDEO_VID_720P_60HZ;
562 else if (res_60 & PS3AV_RESBIT_720x480P)
563 vid = PS3AV_CMD_VIDEO_VID_480P;
564 else
565 vid = -1;
566 }
567 return vid;
568}
569
570static int ps3av_hdmi_get_vid(struct ps3av_info_monitor *info)
571{
572 u32 res_50, res_60;
573 int vid = -1;
574
575 if (info->monitor_type != PS3AV_MONITOR_TYPE_HDMI)
576 return -1;
577
578 /* check native resolution */
579 res_50 = info->res_50.native & PS3AV_RES_MASK_50;
580 res_60 = info->res_60.native & PS3AV_RES_MASK_60;
581 if (res_50 || res_60) {
582 vid = ps3av_resbit2vid(res_50, res_60);
583 return vid;
584 }
585
586 /* check resolution */
587 res_50 = info->res_50.res_bits & PS3AV_RES_MASK_50;
588 res_60 = info->res_60.res_bits & PS3AV_RES_MASK_60;
589 if (res_50 || res_60) {
590 vid = ps3av_resbit2vid(res_50, res_60);
591 return vid;
592 }
593
594 if (ps3av.region & PS3AV_REGION_60)
595 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
596 else
597 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
598 return vid;
599}
600
601static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf,
602 int boot)
603{
604 int i, res, vid = -1, dvi = 0, rgb = 0;
605 struct ps3av_pkt_av_get_monitor_info monitor_info;
606 struct ps3av_info_monitor *info;
607
608 /* get vid for hdmi */
609 for (i = 0; i < av_hw_conf->num_of_hdmi; i++) {
610 res = ps3av_cmd_video_get_monitor_info(&monitor_info,
611 PS3AV_CMD_AVPORT_HDMI_0 +
612 i);
613 if (res < 0)
614 return -1;
615
616 ps3av_cmd_av_monitor_info_dump(&monitor_info);
617 info = &monitor_info.info;
618 /* check DVI */
619 if (info->monitor_type == PS3AV_MONITOR_TYPE_DVI) {
620 dvi = PS3AV_MODE_DVI;
621 break;
622 }
623 /* check HDMI */
624 vid = ps3av_hdmi_get_vid(info);
625 if (vid != -1) {
626 /* got valid vid */
627 break;
628 }
629 }
630
631 if (dvi) {
632 /* DVI mode */
633 vid = PS3AV_DEFAULT_DVI_VID;
634 } else if (vid == -1) {
635 /* no HDMI interface or HDMI is off */
636 if (ps3av.region & PS3AV_REGION_60)
637 vid = PS3AV_DEFAULT_AVMULTI_VID_REG_60;
638 else
639 vid = PS3AV_DEFAULT_AVMULTI_VID_REG_50;
640 if (ps3av.region & PS3AV_REGION_RGB)
641 rgb = PS3AV_MODE_RGB;
642 } else if (boot) {
643 /* HDMI: using DEFAULT HDMI_VID while booting up */
644 info = &monitor_info.info;
645 if (ps3av.region & PS3AV_REGION_60) {
646 if (info->res_60.res_bits & PS3AV_RESBIT_720x480P)
647 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
648 else if (info->res_50.res_bits & PS3AV_RESBIT_720x576P)
649 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
650 else {
651 /* default */
652 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
653 }
654 } else {
655 if (info->res_50.res_bits & PS3AV_RESBIT_720x576P)
656 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
657 else if (info->res_60.res_bits & PS3AV_RESBIT_720x480P)
658 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
659 else {
660 /* default */
661 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
662 }
663 }
664 }
665
666 return (ps3av_vid2table_id(vid) | dvi | rgb);
667}
668
669static int ps3av_get_hw_conf(struct ps3av *ps3av)
670{
671 int i, j, k, res;
672
673 /* get av_hw_conf */
674 res = ps3av_cmd_av_get_hw_conf(&ps3av->av_hw_conf);
675 if (res < 0)
676 return -1;
677
678 ps3av_cmd_av_hw_conf_dump(&ps3av->av_hw_conf);
679
680 for (i = 0; i < PS3AV_HEAD_MAX; i++)
681 ps3av->head[i] = PS3AV_CMD_VIDEO_HEAD_A + i;
682 for (i = 0; i < PS3AV_OPT_PORT_MAX; i++)
683 ps3av->opt_port[i] = PS3AV_CMD_AVPORT_SPDIF_0 + i;
684 for (i = 0; i < ps3av->av_hw_conf.num_of_hdmi; i++)
685 ps3av->av_port[i] = PS3AV_CMD_AVPORT_HDMI_0 + i;
686 for (j = 0; j < ps3av->av_hw_conf.num_of_avmulti; j++)
687 ps3av->av_port[i + j] = PS3AV_CMD_AVPORT_AVMULTI_0 + j;
688 for (k = 0; k < ps3av->av_hw_conf.num_of_spdif; k++)
689 ps3av->av_port[i + j + k] = PS3AV_CMD_AVPORT_SPDIF_0 + k;
690
691 /* set all audio port */
692 ps3av->audio_port = PS3AV_CMD_AUDIO_PORT_HDMI_0
693 | PS3AV_CMD_AUDIO_PORT_HDMI_1
694 | PS3AV_CMD_AUDIO_PORT_AVMULTI_0
695 | PS3AV_CMD_AUDIO_PORT_SPDIF_0 | PS3AV_CMD_AUDIO_PORT_SPDIF_1;
696
697 return 0;
698}
699
700/* set mode using id */
701int ps3av_set_video_mode(u32 id, int boot)
702{
703 int size;
704 u32 option;
705
706 size = ARRAY_SIZE(video_mode_table);
707 if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) {
708 dev_dbg(&ps3av_dev.core, "%s: error id :%d\n", __FUNCTION__,
709 id);
710 return -EINVAL;
711 }
712
713 /* auto mode */
714 option = id & ~PS3AV_MODE_MASK;
715 if ((id & PS3AV_MODE_MASK) == 0) {
716 id = ps3av_auto_videomode(&ps3av.av_hw_conf, boot);
717 if (id < 1) {
718 printk(KERN_ERR "%s: invalid id :%d\n", __FUNCTION__,
719 id);
720 return -EINVAL;
721 }
722 id |= option;
723 }
724
725 /* set videomode */
726 down(&ps3av.pong);
727 ps3av.ps3av_mode_old = ps3av.ps3av_mode;
728 ps3av.ps3av_mode = id;
729 if (ps3av_set_videomode())
730 ps3av.ps3av_mode = ps3av.ps3av_mode_old;
731
732 return 0;
733}
734
735EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
736
737int ps3av_set_mode(u32 id, int boot)
738{
739 int res;
740
741 res = ps3av_set_video_mode(id, boot);
742 if (res)
743 return res;
744
745 res = ps3av_set_audio_mode(PS3AV_CMD_AUDIO_NUM_OF_CH_2,
746 PS3AV_CMD_AUDIO_FS_48K,
747 PS3AV_CMD_AUDIO_WORD_BITS_16,
748 PS3AV_CMD_AUDIO_FORMAT_PCM,
749 PS3AV_CMD_AUDIO_SOURCE_SERIAL);
750 if (res)
751 return res;
752
753 return 0;
754}
755
756EXPORT_SYMBOL_GPL(ps3av_set_mode);
757
758int ps3av_get_mode(void)
759{
760 return ps3av.ps3av_mode;
761}
762
763EXPORT_SYMBOL_GPL(ps3av_get_mode);
764
765int ps3av_get_scanmode(int id)
766{
767 int size;
768
769 id = id & PS3AV_MODE_MASK;
770 size = ARRAY_SIZE(video_mode_table);
771 if (id > size - 1 || id < 0) {
772 printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
773 return -EINVAL;
774 }
775 return video_mode_table[id].interlace;
776}
777
778EXPORT_SYMBOL_GPL(ps3av_get_scanmode);
779
780int ps3av_get_refresh_rate(int id)
781{
782 int size;
783
784 id = id & PS3AV_MODE_MASK;
785 size = ARRAY_SIZE(video_mode_table);
786 if (id > size - 1 || id < 0) {
787 printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
788 return -EINVAL;
789 }
790 return video_mode_table[id].freq;
791}
792
793EXPORT_SYMBOL_GPL(ps3av_get_refresh_rate);
794
795/* get resolution by video_mode */
796int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres)
797{
798 int size;
799
800 id = id & PS3AV_MODE_MASK;
801 size = ARRAY_SIZE(video_mode_table);
802 if (id > size - 1 || id < 0) {
803 printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
804 return -EINVAL;
805 }
806 *xres = video_mode_table[id].x;
807 *yres = video_mode_table[id].y;
808 return 0;
809}
810
811EXPORT_SYMBOL_GPL(ps3av_video_mode2res);
812
813/* mute */
814int ps3av_video_mute(int mute)
815{
816 return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON
817 : PS3AV_CMD_MUTE_OFF);
818}
819
820EXPORT_SYMBOL_GPL(ps3av_video_mute);
821
822int ps3av_audio_mute(int mute)
823{
824 return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
825 : PS3AV_CMD_MUTE_OFF);
826}
827
828EXPORT_SYMBOL_GPL(ps3av_audio_mute);
829
830int ps3av_dev_open(void)
831{
832 int status = 0;
833
834 mutex_lock(&ps3av.mutex);
835 if (!ps3av.open_count++) {
836 status = lv1_gpu_open(0);
837 if (status) {
838 printk(KERN_ERR "%s: lv1_gpu_open failed %d\n",
839 __FUNCTION__, status);
840 ps3av.open_count--;
841 }
842 }
843 mutex_unlock(&ps3av.mutex);
844
845 return status;
846}
847
848EXPORT_SYMBOL_GPL(ps3av_dev_open);
849
850int ps3av_dev_close(void)
851{
852 int status = 0;
853
854 mutex_lock(&ps3av.mutex);
855 if (ps3av.open_count <= 0) {
856 printk(KERN_ERR "%s: GPU already closed\n", __FUNCTION__);
857 status = -1;
858 } else if (!--ps3av.open_count) {
859 status = lv1_gpu_close();
860 if (status)
861 printk(KERN_WARNING "%s: lv1_gpu_close failed %d\n",
862 __FUNCTION__, status);
863 }
864 mutex_unlock(&ps3av.mutex);
865
866 return status;
867}
868
869EXPORT_SYMBOL_GPL(ps3av_dev_close);
870
871static int ps3av_probe(struct ps3_vuart_port_device *dev)
872{
873 int res;
874 u32 id;
875
876 dev_dbg(&ps3av_dev.core, "init ...\n");
877 dev_dbg(&ps3av_dev.core, " timeout=%d\n", timeout);
878
879 memset(&ps3av, 0, sizeof(ps3av));
880
881 init_MUTEX(&ps3av.sem);
882 init_MUTEX_LOCKED(&ps3av.ping);
883 init_MUTEX(&ps3av.pong);
884 mutex_init(&ps3av.mutex);
885 ps3av.ps3av_mode = 0;
886 ps3av.dev = dev;
887 kernel_thread(ps3avd, &ps3av, CLONE_KERNEL);
888
889 ps3av.available = 1;
890 switch (ps3_os_area_get_av_multi_out()) {
891 case PS3_PARAM_AV_MULTI_OUT_NTSC:
892 ps3av.region = PS3AV_REGION_60;
893 break;
894 case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR:
895 case PS3_PARAM_AV_MULTI_OUT_SECAM:
896 ps3av.region = PS3AV_REGION_50;
897 break;
898 case PS3_PARAM_AV_MULTI_OUT_PAL_RGB:
899 ps3av.region = PS3AV_REGION_50 | PS3AV_REGION_RGB;
900 break;
901 default:
902 ps3av.region = PS3AV_REGION_60;
903 break;
904 }
905
906 /* init avsetting modules */
907 res = ps3av_cmd_init();
908 if (res < 0)
909 printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __FUNCTION__,
910 res);
911
912 ps3av_get_hw_conf(&ps3av);
913 id = ps3av_auto_videomode(&ps3av.av_hw_conf, 1);
914 mutex_lock(&ps3av.mutex);
915 ps3av.ps3av_mode = id;
916 mutex_unlock(&ps3av.mutex);
917
918 dev_dbg(&ps3av_dev.core, "init...done\n");
919
920 return 0;
921}
922
923static int ps3av_remove(struct ps3_vuart_port_device *dev)
924{
925 if (ps3av.available) {
926 ps3av_cmd_fin();
927 ps3av.available = 0;
928 }
929
930 return 0;
931}
932
933static void ps3av_shutdown(struct ps3_vuart_port_device *dev)
934{
935 ps3av_remove(dev);
936}
937
938static struct ps3_vuart_port_driver ps3av_driver = {
939 .match_id = PS3_MATCH_ID_AV_SETTINGS,
940 .core = {
941 .name = "ps3_av",
942 },
943 .probe = ps3av_probe,
944 .remove = ps3av_remove,
945 .shutdown = ps3av_shutdown,
946};
947
948static int ps3av_module_init(void)
949{
950 int error = ps3_vuart_port_driver_register(&ps3av_driver);
951 if (error) {
952 printk(KERN_ERR
953 "%s: ps3_vuart_port_driver_register failed %d\n",
954 __FUNCTION__, error);
955 return error;
956 }
957
958 error = ps3_vuart_port_device_register(&ps3av_dev);
959 if (error)
960 printk(KERN_ERR
961 "%s: ps3_vuart_port_device_register failed %d\n",
962 __FUNCTION__, error);
963
964 return error;
965}
966
967static void __exit ps3av_module_exit(void)
968{
969 device_unregister(&ps3av_dev.core);
970 ps3_vuart_port_driver_unregister(&ps3av_driver);
971}
972
973subsys_initcall(ps3av_module_init);
974module_exit(ps3av_module_exit);