[PATCH] ps3: disable display flipping during mode changes
[linux-2.6.git] / drivers / ps3 / ps3av.c
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
36 static int timeout = 5000;      /* in msec ( 5 sec ) */
37 module_param(timeout, int, 0644);
38
39 static struct ps3av ps3av;
40
41 static 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
53 static 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 */
80 static 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
131 static 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
146 static 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
166 static 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
222 static 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
240 void 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
247 int 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
300 static 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
316 static 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
350 static 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
372 int 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
433 EXPORT_SYMBOL_GPL(ps3av_set_audio_mode);
434
435 static 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
446 static 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
516 static 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
530 static 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
540 static 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
570 static 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
601 static 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
669 static 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 */
701 int 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
735 EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
736
737 int 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
756 EXPORT_SYMBOL_GPL(ps3av_set_mode);
757
758 int ps3av_get_mode(void)
759 {
760         return ps3av.ps3av_mode;
761 }
762
763 EXPORT_SYMBOL_GPL(ps3av_get_mode);
764
765 int 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
778 EXPORT_SYMBOL_GPL(ps3av_get_scanmode);
779
780 int 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
793 EXPORT_SYMBOL_GPL(ps3av_get_refresh_rate);
794
795 /* get resolution by video_mode */
796 int 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
811 EXPORT_SYMBOL_GPL(ps3av_video_mode2res);
812
813 /* mute */
814 int 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
820 EXPORT_SYMBOL_GPL(ps3av_video_mute);
821
822 int ps3av_audio_mute(int mute)
823 {
824         return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
825                                          : PS3AV_CMD_MUTE_OFF);
826 }
827
828 EXPORT_SYMBOL_GPL(ps3av_audio_mute);
829
830 int 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
848 EXPORT_SYMBOL_GPL(ps3av_dev_open);
849
850 int 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
869 EXPORT_SYMBOL_GPL(ps3av_dev_close);
870
871 static 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
923 static 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
933 static void ps3av_shutdown(struct ps3_vuart_port_device *dev)
934 {
935         ps3av_remove(dev);
936 }
937
938 static 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
948 static 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
967 static void __exit ps3av_module_exit(void)
968 {
969         device_unregister(&ps3av_dev.core);
970         ps3_vuart_port_driver_unregister(&ps3av_driver);
971 }
972
973 subsys_initcall(ps3av_module_init);
974 module_exit(ps3av_module_exit);