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