d318eabce3c32e012ad64619a07f2a5403657489
[safe/jmp/linux-2.6] / 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         queue_work(ps3av.wq, &ps3av.work);
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 void ps3avd(struct work_struct *work)
519 {
520         ps3av_set_videomode_cont(ps3av.ps3av_mode, ps3av.ps3av_mode_old);
521         complete(&ps3av.done);
522 }
523
524 static int ps3av_vid2table_id(int vid)
525 {
526         int i;
527
528         for (i = 1; i < ARRAY_SIZE(video_mode_table); i++)
529                 if (video_mode_table[i].vid == vid)
530                         return i;
531         return -1;
532 }
533
534 static int ps3av_resbit2vid(u32 res_50, u32 res_60)
535 {
536         int vid = -1;
537
538         if (res_50 > res_60) {  /* if res_50 == res_60, res_60 will be used */
539                 if (res_50 & PS3AV_RESBIT_1920x1080P)
540                         vid = PS3AV_CMD_VIDEO_VID_1080P_50HZ;
541                 else if (res_50 & PS3AV_RESBIT_1920x1080I)
542                         vid = PS3AV_CMD_VIDEO_VID_1080I_50HZ;
543                 else if (res_50 & PS3AV_RESBIT_1280x720P)
544                         vid = PS3AV_CMD_VIDEO_VID_720P_50HZ;
545                 else if (res_50 & PS3AV_RESBIT_720x576P)
546                         vid = PS3AV_CMD_VIDEO_VID_576P;
547                 else
548                         vid = -1;
549         } else {
550                 if (res_60 & PS3AV_RESBIT_1920x1080P)
551                         vid = PS3AV_CMD_VIDEO_VID_1080P_60HZ;
552                 else if (res_60 & PS3AV_RESBIT_1920x1080I)
553                         vid = PS3AV_CMD_VIDEO_VID_1080I_60HZ;
554                 else if (res_60 & PS3AV_RESBIT_1280x720P)
555                         vid = PS3AV_CMD_VIDEO_VID_720P_60HZ;
556                 else if (res_60 & PS3AV_RESBIT_720x480P)
557                         vid = PS3AV_CMD_VIDEO_VID_480P;
558                 else
559                         vid = -1;
560         }
561         return vid;
562 }
563
564 static int ps3av_hdmi_get_vid(struct ps3av_info_monitor *info)
565 {
566         u32 res_50, res_60;
567         int vid = -1;
568
569         if (info->monitor_type != PS3AV_MONITOR_TYPE_HDMI)
570                 return -1;
571
572         /* check native resolution */
573         res_50 = info->res_50.native & PS3AV_RES_MASK_50;
574         res_60 = info->res_60.native & PS3AV_RES_MASK_60;
575         if (res_50 || res_60) {
576                 vid = ps3av_resbit2vid(res_50, res_60);
577                 return vid;
578         }
579
580         /* check resolution */
581         res_50 = info->res_50.res_bits & PS3AV_RES_MASK_50;
582         res_60 = info->res_60.res_bits & PS3AV_RES_MASK_60;
583         if (res_50 || res_60) {
584                 vid = ps3av_resbit2vid(res_50, res_60);
585                 return vid;
586         }
587
588         if (ps3av.region & PS3AV_REGION_60)
589                 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
590         else
591                 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
592         return vid;
593 }
594
595 static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf,
596                                 int boot)
597 {
598         int i, res, vid = -1, dvi = 0, rgb = 0;
599         struct ps3av_pkt_av_get_monitor_info monitor_info;
600         struct ps3av_info_monitor *info;
601
602         /* get vid for hdmi */
603         for (i = 0; i < av_hw_conf->num_of_hdmi; i++) {
604                 res = ps3av_cmd_video_get_monitor_info(&monitor_info,
605                                                        PS3AV_CMD_AVPORT_HDMI_0 +
606                                                        i);
607                 if (res < 0)
608                         return -1;
609
610                 ps3av_cmd_av_monitor_info_dump(&monitor_info);
611                 info = &monitor_info.info;
612                 /* check DVI */
613                 if (info->monitor_type == PS3AV_MONITOR_TYPE_DVI) {
614                         dvi = PS3AV_MODE_DVI;
615                         break;
616                 }
617                 /* check HDMI */
618                 vid = ps3av_hdmi_get_vid(info);
619                 if (vid != -1) {
620                         /* got valid vid */
621                         break;
622                 }
623         }
624
625         if (dvi) {
626                 /* DVI mode */
627                 vid = PS3AV_DEFAULT_DVI_VID;
628         } else if (vid == -1) {
629                 /* no HDMI interface or HDMI is off */
630                 if (ps3av.region & PS3AV_REGION_60)
631                         vid = PS3AV_DEFAULT_AVMULTI_VID_REG_60;
632                 else
633                         vid = PS3AV_DEFAULT_AVMULTI_VID_REG_50;
634                 if (ps3av.region & PS3AV_REGION_RGB)
635                         rgb = PS3AV_MODE_RGB;
636         } else if (boot) {
637                 /* HDMI: using DEFAULT HDMI_VID while booting up */
638                 info = &monitor_info.info;
639                 if (ps3av.region & PS3AV_REGION_60) {
640                         if (info->res_60.res_bits & PS3AV_RESBIT_720x480P)
641                                 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
642                         else if (info->res_50.res_bits & PS3AV_RESBIT_720x576P)
643                                 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
644                         else {
645                                 /* default */
646                                 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
647                         }
648                 } else {
649                         if (info->res_50.res_bits & PS3AV_RESBIT_720x576P)
650                                 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
651                         else if (info->res_60.res_bits & PS3AV_RESBIT_720x480P)
652                                 vid = PS3AV_DEFAULT_HDMI_VID_REG_60;
653                         else {
654                                 /* default */
655                                 vid = PS3AV_DEFAULT_HDMI_VID_REG_50;
656                         }
657                 }
658         }
659
660         return (ps3av_vid2table_id(vid) | dvi | rgb);
661 }
662
663 static int ps3av_get_hw_conf(struct ps3av *ps3av)
664 {
665         int i, j, k, res;
666
667         /* get av_hw_conf */
668         res = ps3av_cmd_av_get_hw_conf(&ps3av->av_hw_conf);
669         if (res < 0)
670                 return -1;
671
672         ps3av_cmd_av_hw_conf_dump(&ps3av->av_hw_conf);
673
674         for (i = 0; i < PS3AV_HEAD_MAX; i++)
675                 ps3av->head[i] = PS3AV_CMD_VIDEO_HEAD_A + i;
676         for (i = 0; i < PS3AV_OPT_PORT_MAX; i++)
677                 ps3av->opt_port[i] = PS3AV_CMD_AVPORT_SPDIF_0 + i;
678         for (i = 0; i < ps3av->av_hw_conf.num_of_hdmi; i++)
679                 ps3av->av_port[i] = PS3AV_CMD_AVPORT_HDMI_0 + i;
680         for (j = 0; j < ps3av->av_hw_conf.num_of_avmulti; j++)
681                 ps3av->av_port[i + j] = PS3AV_CMD_AVPORT_AVMULTI_0 + j;
682         for (k = 0; k < ps3av->av_hw_conf.num_of_spdif; k++)
683                 ps3av->av_port[i + j + k] = PS3AV_CMD_AVPORT_SPDIF_0 + k;
684
685         /* set all audio port */
686         ps3av->audio_port = PS3AV_CMD_AUDIO_PORT_HDMI_0
687             | PS3AV_CMD_AUDIO_PORT_HDMI_1
688             | PS3AV_CMD_AUDIO_PORT_AVMULTI_0
689             | PS3AV_CMD_AUDIO_PORT_SPDIF_0 | PS3AV_CMD_AUDIO_PORT_SPDIF_1;
690
691         return 0;
692 }
693
694 /* set mode using id */
695 int ps3av_set_video_mode(u32 id, int boot)
696 {
697         int size;
698         u32 option;
699
700         size = ARRAY_SIZE(video_mode_table);
701         if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) {
702                 dev_dbg(&ps3av_dev.core, "%s: error id :%d\n", __FUNCTION__,
703                         id);
704                 return -EINVAL;
705         }
706
707         /* auto mode */
708         option = id & ~PS3AV_MODE_MASK;
709         if ((id & PS3AV_MODE_MASK) == 0) {
710                 id = ps3av_auto_videomode(&ps3av.av_hw_conf, boot);
711                 if (id < 1) {
712                         printk(KERN_ERR "%s: invalid id :%d\n", __FUNCTION__,
713                                id);
714                         return -EINVAL;
715                 }
716                 id |= option;
717         }
718
719         /* set videomode */
720         wait_for_completion(&ps3av.done);
721         ps3av.ps3av_mode_old = ps3av.ps3av_mode;
722         ps3av.ps3av_mode = id;
723         if (ps3av_set_videomode())
724                 ps3av.ps3av_mode = ps3av.ps3av_mode_old;
725
726         return 0;
727 }
728
729 EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
730
731 int ps3av_set_mode(u32 id, int boot)
732 {
733         int res;
734
735         res = ps3av_set_video_mode(id, boot);
736         if (res)
737                 return res;
738
739         res = ps3av_set_audio_mode(PS3AV_CMD_AUDIO_NUM_OF_CH_2,
740                                    PS3AV_CMD_AUDIO_FS_48K,
741                                    PS3AV_CMD_AUDIO_WORD_BITS_16,
742                                    PS3AV_CMD_AUDIO_FORMAT_PCM,
743                                    PS3AV_CMD_AUDIO_SOURCE_SERIAL);
744         if (res)
745                 return res;
746
747         return 0;
748 }
749
750 EXPORT_SYMBOL_GPL(ps3av_set_mode);
751
752 int ps3av_get_mode(void)
753 {
754         return ps3av.ps3av_mode;
755 }
756
757 EXPORT_SYMBOL_GPL(ps3av_get_mode);
758
759 int ps3av_get_scanmode(int id)
760 {
761         int size;
762
763         id = id & PS3AV_MODE_MASK;
764         size = ARRAY_SIZE(video_mode_table);
765         if (id > size - 1 || id < 0) {
766                 printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
767                 return -EINVAL;
768         }
769         return video_mode_table[id].interlace;
770 }
771
772 EXPORT_SYMBOL_GPL(ps3av_get_scanmode);
773
774 int ps3av_get_refresh_rate(int id)
775 {
776         int size;
777
778         id = id & PS3AV_MODE_MASK;
779         size = ARRAY_SIZE(video_mode_table);
780         if (id > size - 1 || id < 0) {
781                 printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
782                 return -EINVAL;
783         }
784         return video_mode_table[id].freq;
785 }
786
787 EXPORT_SYMBOL_GPL(ps3av_get_refresh_rate);
788
789 /* get resolution by video_mode */
790 int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres)
791 {
792         int size;
793
794         id = id & PS3AV_MODE_MASK;
795         size = ARRAY_SIZE(video_mode_table);
796         if (id > size - 1 || id < 0) {
797                 printk(KERN_ERR "%s: invalid mode %d\n", __FUNCTION__, id);
798                 return -EINVAL;
799         }
800         *xres = video_mode_table[id].x;
801         *yres = video_mode_table[id].y;
802         return 0;
803 }
804
805 EXPORT_SYMBOL_GPL(ps3av_video_mode2res);
806
807 /* mute */
808 int ps3av_video_mute(int mute)
809 {
810         return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON
811                                             : PS3AV_CMD_MUTE_OFF);
812 }
813
814 EXPORT_SYMBOL_GPL(ps3av_video_mute);
815
816 int ps3av_audio_mute(int mute)
817 {
818         return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
819                                          : PS3AV_CMD_MUTE_OFF);
820 }
821
822 EXPORT_SYMBOL_GPL(ps3av_audio_mute);
823
824 int ps3av_dev_open(void)
825 {
826         int status = 0;
827
828         mutex_lock(&ps3av.mutex);
829         if (!ps3av.open_count++) {
830                 status = lv1_gpu_open(0);
831                 if (status) {
832                         printk(KERN_ERR "%s: lv1_gpu_open failed %d\n",
833                                __FUNCTION__, status);
834                         ps3av.open_count--;
835                 }
836         }
837         mutex_unlock(&ps3av.mutex);
838
839         return status;
840 }
841
842 EXPORT_SYMBOL_GPL(ps3av_dev_open);
843
844 int ps3av_dev_close(void)
845 {
846         int status = 0;
847
848         mutex_lock(&ps3av.mutex);
849         if (ps3av.open_count <= 0) {
850                 printk(KERN_ERR "%s: GPU already closed\n", __FUNCTION__);
851                 status = -1;
852         } else if (!--ps3av.open_count) {
853                 status = lv1_gpu_close();
854                 if (status)
855                         printk(KERN_WARNING "%s: lv1_gpu_close failed %d\n",
856                                __FUNCTION__, status);
857         }
858         mutex_unlock(&ps3av.mutex);
859
860         return status;
861 }
862
863 EXPORT_SYMBOL_GPL(ps3av_dev_close);
864
865 static int ps3av_probe(struct ps3_vuart_port_device *dev)
866 {
867         int res;
868         u32 id;
869
870         dev_dbg(&ps3av_dev.core, "init ...\n");
871         dev_dbg(&ps3av_dev.core, "  timeout=%d\n", timeout);
872
873         memset(&ps3av, 0, sizeof(ps3av));
874
875         init_MUTEX(&ps3av.sem);
876         mutex_init(&ps3av.mutex);
877         ps3av.ps3av_mode = 0;
878         ps3av.dev = dev;
879
880         INIT_WORK(&ps3av.work, ps3avd);
881         init_completion(&ps3av.done);
882         complete(&ps3av.done);
883         ps3av.wq = create_singlethread_workqueue("ps3avd");
884         if (!ps3av.wq)
885                 return -ENOMEM;
886
887         ps3av.available = 1;
888         switch (ps3_os_area_get_av_multi_out()) {
889         case PS3_PARAM_AV_MULTI_OUT_NTSC:
890                 ps3av.region = PS3AV_REGION_60;
891                 break;
892         case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR:
893         case PS3_PARAM_AV_MULTI_OUT_SECAM:
894                 ps3av.region = PS3AV_REGION_50;
895                 break;
896         case PS3_PARAM_AV_MULTI_OUT_PAL_RGB:
897                 ps3av.region = PS3AV_REGION_50 | PS3AV_REGION_RGB;
898                 break;
899         default:
900                 ps3av.region = PS3AV_REGION_60;
901                 break;
902         }
903
904         /* init avsetting modules */
905         res = ps3av_cmd_init();
906         if (res < 0)
907                 printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __FUNCTION__,
908                        res);
909
910         ps3av_get_hw_conf(&ps3av);
911         id = ps3av_auto_videomode(&ps3av.av_hw_conf, 1);
912         mutex_lock(&ps3av.mutex);
913         ps3av.ps3av_mode = id;
914         mutex_unlock(&ps3av.mutex);
915
916         dev_dbg(&ps3av_dev.core, "init...done\n");
917
918         return 0;
919 }
920
921 static int ps3av_remove(struct ps3_vuart_port_device *dev)
922 {
923         if (ps3av.available) {
924                 ps3av_cmd_fin();
925                 if (ps3av.wq)
926                         destroy_workqueue(ps3av.wq);
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;
951
952         if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
953                 return -ENODEV;
954
955         error = ps3_vuart_port_driver_register(&ps3av_driver);
956         if (error) {
957                 printk(KERN_ERR
958                        "%s: ps3_vuart_port_driver_register failed %d\n",
959                        __FUNCTION__, error);
960                 return error;
961         }
962
963         error = ps3_vuart_port_device_register(&ps3av_dev);
964         if (error)
965                 printk(KERN_ERR
966                        "%s: ps3_vuart_port_device_register failed %d\n",
967                        __FUNCTION__, error);
968
969         return error;
970 }
971
972 static void __exit ps3av_module_exit(void)
973 {
974         device_unregister(&ps3av_dev.core);
975         ps3_vuart_port_driver_unregister(&ps3av_driver);
976 }
977
978 subsys_initcall(ps3av_module_init);
979 module_exit(ps3av_module_exit);