1063 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PS3 AV backend support.4 *5 * Copyright (C) 2007 Sony Computer Entertainment Inc.6 * Copyright 2007 Sony Corp.7 */8 9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/delay.h>12#include <linux/notifier.h>13#include <linux/ioctl.h>14#include <linux/slab.h>15 16#include <asm/firmware.h>17#include <asm/ps3av.h>18#include <asm/ps3.h>19 20#include <video/cmdline.h>21 22#include "vuart.h"23 24#define BUFSIZE 4096 /* vuart buf size */25#define PS3AV_BUF_SIZE 512 /* max packet size */26 27static int safe_mode;28 29static int timeout = 5000; /* in msec ( 5 sec ) */30module_param(timeout, int, 0644);31 32static struct ps3av {33 struct mutex mutex;34 struct work_struct work;35 struct completion done;36 int open_count;37 struct ps3_system_bus_device *dev;38 39 int region;40 struct ps3av_pkt_av_get_hw_conf av_hw_conf;41 u32 av_port[PS3AV_AV_PORT_MAX + PS3AV_OPT_PORT_MAX];42 u32 opt_port[PS3AV_OPT_PORT_MAX];43 u32 head[PS3AV_HEAD_MAX];44 u32 audio_port;45 int ps3av_mode;46 int ps3av_mode_old;47 union {48 struct ps3av_reply_hdr reply_hdr;49 u8 raw[PS3AV_BUF_SIZE];50 } recv_buf;51} *ps3av;52 53/* color space */54#define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_855#define RGB8 PS3AV_CMD_VIDEO_CS_RGB_856/* format */57#define XRGB PS3AV_CMD_VIDEO_FMT_X8R8G8B858/* aspect */59#define A_N PS3AV_CMD_AV_ASPECT_4_360#define A_W PS3AV_CMD_AV_ASPECT_16_961static const struct avset_video_mode {62 u32 cs;63 u32 fmt;64 u32 vid;65 u32 aspect;66 u32 x;67 u32 y;68} video_mode_table[] = {69 { 0, }, /* auto */70 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480},71 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480},72 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_W, 1280, 720},73 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080},74 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080},75 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576},76 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576},77 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_W, 1280, 720},78 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080},79 {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080},80 { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768},81 { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024},82 { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200},83};84 85/* supported CIDs */86static u32 cmd_table[] = {87 /* init */88 PS3AV_CID_AV_INIT,89 PS3AV_CID_AV_FIN,90 PS3AV_CID_VIDEO_INIT,91 PS3AV_CID_AUDIO_INIT,92 93 /* set */94 PS3AV_CID_AV_ENABLE_EVENT,95 PS3AV_CID_AV_DISABLE_EVENT,96 97 PS3AV_CID_AV_VIDEO_CS,98 PS3AV_CID_AV_VIDEO_MUTE,99 PS3AV_CID_AV_VIDEO_DISABLE_SIG,100 PS3AV_CID_AV_AUDIO_PARAM,101 PS3AV_CID_AV_AUDIO_MUTE,102 PS3AV_CID_AV_HDMI_MODE,103 PS3AV_CID_AV_TV_MUTE,104 105 PS3AV_CID_VIDEO_MODE,106 PS3AV_CID_VIDEO_FORMAT,107 PS3AV_CID_VIDEO_PITCH,108 109 PS3AV_CID_AUDIO_MODE,110 PS3AV_CID_AUDIO_MUTE,111 PS3AV_CID_AUDIO_ACTIVE,112 PS3AV_CID_AUDIO_INACTIVE,113 PS3AV_CID_AVB_PARAM,114 115 /* get */116 PS3AV_CID_AV_GET_HW_CONF,117 PS3AV_CID_AV_GET_MONITOR_INFO,118 119 /* event */120 PS3AV_CID_EVENT_UNPLUGGED,121 PS3AV_CID_EVENT_PLUGGED,122 PS3AV_CID_EVENT_HDCP_DONE,123 PS3AV_CID_EVENT_HDCP_FAIL,124 PS3AV_CID_EVENT_HDCP_AUTH,125 PS3AV_CID_EVENT_HDCP_ERROR,126 127 0128};129 130#define PS3AV_EVENT_CMD_MASK 0x10000000131#define PS3AV_EVENT_ID_MASK 0x0000ffff132#define PS3AV_CID_MASK 0xffffffff133#define PS3AV_REPLY_BIT 0x80000000134 135#define ps3av_event_get_port_id(cid) ((cid >> 16) & 0xff)136 137static u32 *ps3av_search_cmd_table(u32 cid, u32 mask)138{139 u32 *table;140 int i;141 142 table = cmd_table;143 for (i = 0;; table++, i++) {144 if ((*table & mask) == (cid & mask))145 break;146 if (*table == 0)147 return NULL;148 }149 return table;150}151 152static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr)153{154 u32 *table;155 156 if (hdr->cid & PS3AV_EVENT_CMD_MASK) {157 table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK);158 if (table)159 dev_dbg(&ps3av->dev->core,160 "recv event packet cid:%08x port:0x%x size:%d\n",161 hdr->cid, ps3av_event_get_port_id(hdr->cid),162 hdr->size);163 else164 printk(KERN_ERR165 "%s: failed event packet, cid:%08x size:%d\n",166 __func__, hdr->cid, hdr->size);167 return 1; /* receive event packet */168 }169 return 0;170}171 172 173#define POLLING_INTERVAL 25 /* in msec */174 175static int ps3av_vuart_write(struct ps3_system_bus_device *dev,176 const void *buf, unsigned long size)177{178 int error;179 dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);180 error = ps3_vuart_write(dev, buf, size);181 dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);182 return error ? error : size;183}184 185static int ps3av_vuart_read(struct ps3_system_bus_device *dev, void *buf,186 unsigned long size, int timeout)187{188 int error;189 int loopcnt = 0;190 191 dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);192 timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL;193 while (loopcnt++ <= timeout) {194 error = ps3_vuart_read(dev, buf, size);195 if (!error)196 return size;197 if (error != -EAGAIN) {198 printk(KERN_ERR "%s: ps3_vuart_read failed %d\n",199 __func__, error);200 return error;201 }202 msleep(POLLING_INTERVAL);203 }204 return -EWOULDBLOCK;205}206 207static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,208 struct ps3av_reply_hdr *recv_buf, int write_len,209 int read_len)210{211 int res;212 u32 cmd;213 int event;214 215 if (!ps3av)216 return -ENODEV;217 218 /* send pkt */219 res = ps3av_vuart_write(ps3av->dev, send_buf, write_len);220 if (res < 0) {221 dev_warn(&ps3av->dev->core,222 "%s:%d: ps3av_vuart_write() failed: %s\n", __func__,223 __LINE__, ps3_result(res));224 return res;225 }226 227 /* recv pkt */228 cmd = send_buf->cid;229 do {230 /* read header */231 res = ps3av_vuart_read(ps3av->dev, recv_buf, PS3AV_HDR_SIZE,232 timeout);233 if (res != PS3AV_HDR_SIZE) {234 dev_warn(&ps3av->dev->core,235 "%s:%d: ps3av_vuart_read() failed: %s\n", __func__,236 __LINE__, ps3_result(res));237 return res;238 }239 240 /* read body */241 res = ps3av_vuart_read(ps3av->dev, &recv_buf->cid,242 recv_buf->size, timeout);243 if (res < 0) {244 dev_warn(&ps3av->dev->core,245 "%s:%d: ps3av_vuart_read() failed: %s\n", __func__,246 __LINE__, ps3_result(res));247 return res;248 }249 res += PS3AV_HDR_SIZE; /* total len */250 event = ps3av_parse_event_packet(recv_buf);251 /* ret > 0 event packet */252 } while (event);253 254 if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) {255 dev_warn(&ps3av->dev->core, "%s:%d: reply err: %x\n", __func__,256 __LINE__, recv_buf->cid);257 return -EINVAL;258 }259 260 return 0;261}262 263static int ps3av_process_reply_packet(struct ps3av_send_hdr *cmd_buf,264 const struct ps3av_reply_hdr *recv_buf,265 int user_buf_size)266{267 int return_len;268 269 if (recv_buf->version != PS3AV_VERSION) {270 dev_dbg(&ps3av->dev->core, "reply_packet invalid version:%x\n",271 recv_buf->version);272 return -EFAULT;273 }274 return_len = recv_buf->size + PS3AV_HDR_SIZE;275 if (return_len > user_buf_size)276 return_len = user_buf_size;277 memcpy(cmd_buf, recv_buf, return_len);278 return 0; /* success */279}280 281void ps3av_set_hdr(u32 cid, u16 size, struct ps3av_send_hdr *hdr)282{283 hdr->version = PS3AV_VERSION;284 hdr->size = size - PS3AV_HDR_SIZE;285 hdr->cid = cid;286}287 288int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size,289 struct ps3av_send_hdr *buf)290{291 int res = 0;292 u32 *table;293 294 BUG_ON(!ps3av);295 296 mutex_lock(&ps3av->mutex);297 298 table = ps3av_search_cmd_table(cid, PS3AV_CID_MASK);299 BUG_ON(!table);300 BUG_ON(send_len < PS3AV_HDR_SIZE);301 BUG_ON(usr_buf_size < send_len);302 BUG_ON(usr_buf_size > PS3AV_BUF_SIZE);303 304 /* create header */305 ps3av_set_hdr(cid, send_len, buf);306 307 /* send packet via vuart */308 res = ps3av_send_cmd_pkt(buf, &ps3av->recv_buf.reply_hdr, send_len,309 usr_buf_size);310 if (res < 0) {311 printk(KERN_ERR312 "%s: ps3av_send_cmd_pkt() failed (result=%d)\n",313 __func__, res);314 goto err;315 }316 317 /* process reply packet */318 res = ps3av_process_reply_packet(buf, &ps3av->recv_buf.reply_hdr,319 usr_buf_size);320 if (res < 0) {321 printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n",322 __func__, res);323 goto err;324 }325 326 mutex_unlock(&ps3av->mutex);327 return 0;328 329err:330 mutex_unlock(&ps3av->mutex);331 printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res);332 return res;333}334 335static int ps3av_set_av_video_mute(u32 mute)336{337 int i, num_of_av_port, res;338 339 num_of_av_port = ps3av->av_hw_conf.num_of_hdmi +340 ps3av->av_hw_conf.num_of_avmulti;341 /* video mute on */342 for (i = 0; i < num_of_av_port; i++) {343 res = ps3av_cmd_av_video_mute(1, &ps3av->av_port[i], mute);344 if (res < 0)345 return -1;346 }347 348 return 0;349}350 351static int ps3av_set_video_disable_sig(void)352{353 int i, num_of_hdmi_port, num_of_av_port, res;354 355 num_of_hdmi_port = ps3av->av_hw_conf.num_of_hdmi;356 num_of_av_port = ps3av->av_hw_conf.num_of_hdmi +357 ps3av->av_hw_conf.num_of_avmulti;358 359 /* tv mute */360 for (i = 0; i < num_of_hdmi_port; i++) {361 res = ps3av_cmd_av_tv_mute(ps3av->av_port[i],362 PS3AV_CMD_MUTE_ON);363 if (res < 0)364 return -1;365 }366 msleep(100);367 368 /* video mute on */369 for (i = 0; i < num_of_av_port; i++) {370 res = ps3av_cmd_av_video_disable_sig(ps3av->av_port[i]);371 if (res < 0)372 return -1;373 if (i < num_of_hdmi_port) {374 res = ps3av_cmd_av_tv_mute(ps3av->av_port[i],375 PS3AV_CMD_MUTE_OFF);376 if (res < 0)377 return -1;378 }379 }380 msleep(300);381 382 return 0;383}384 385static int ps3av_set_audio_mute(u32 mute)386{387 int i, num_of_av_port, num_of_opt_port, res;388 389 num_of_av_port = ps3av->av_hw_conf.num_of_hdmi +390 ps3av->av_hw_conf.num_of_avmulti;391 num_of_opt_port = ps3av->av_hw_conf.num_of_spdif;392 393 for (i = 0; i < num_of_av_port; i++) {394 res = ps3av_cmd_av_audio_mute(1, &ps3av->av_port[i], mute);395 if (res < 0)396 return -1;397 }398 for (i = 0; i < num_of_opt_port; i++) {399 res = ps3av_cmd_audio_mute(1, &ps3av->opt_port[i], mute);400 if (res < 0)401 return -1;402 }403 404 return 0;405}406 407int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source)408{409 struct ps3av_pkt_avb_param avb_param;410 int i, num_of_audio, vid, res;411 struct ps3av_pkt_audio_mode audio_mode;412 u32 len = 0;413 414 num_of_audio = ps3av->av_hw_conf.num_of_hdmi +415 ps3av->av_hw_conf.num_of_avmulti +416 ps3av->av_hw_conf.num_of_spdif;417 418 avb_param.num_of_video_pkt = 0;419 avb_param.num_of_audio_pkt = PS3AV_AVB_NUM_AUDIO; /* always 0 */420 avb_param.num_of_av_video_pkt = 0;421 avb_param.num_of_av_audio_pkt = ps3av->av_hw_conf.num_of_hdmi;422 423 vid = video_mode_table[ps3av->ps3av_mode].vid;424 425 /* audio mute */426 ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON);427 428 /* audio inactive */429 res = ps3av_cmd_audio_active(0, ps3av->audio_port);430 if (res < 0)431 dev_dbg(&ps3av->dev->core,432 "ps3av_cmd_audio_active OFF failed\n");433 434 /* audio_pkt */435 for (i = 0; i < num_of_audio; i++) {436 ps3av_cmd_set_audio_mode(&audio_mode, ps3av->av_port[i], ch,437 fs, word_bits, format, source);438 if (i < ps3av->av_hw_conf.num_of_hdmi) {439 /* hdmi only */440 len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len],441 ps3av->av_port[i],442 &audio_mode, vid);443 }444 /* audio_mode pkt should be sent separately */445 res = ps3av_cmd_audio_mode(&audio_mode);446 if (res < 0)447 dev_dbg(&ps3av->dev->core,448 "ps3av_cmd_audio_mode failed, port:%x\n", i);449 }450 451 /* send command using avb pkt */452 len += offsetof(struct ps3av_pkt_avb_param, buf);453 res = ps3av_cmd_avb_param(&avb_param, len);454 if (res < 0)455 dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n");456 457 /* audio mute */458 ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF);459 460 /* audio active */461 res = ps3av_cmd_audio_active(1, ps3av->audio_port);462 if (res < 0)463 dev_dbg(&ps3av->dev->core,464 "ps3av_cmd_audio_active ON failed\n");465 466 return 0;467}468EXPORT_SYMBOL_GPL(ps3av_set_audio_mode);469 470static int ps3av_set_videomode(void)471{472 /* av video mute */473 ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON);474 475 /* wake up ps3avd to do the actual video mode setting */476 schedule_work(&ps3av->work);477 478 return 0;479}480 481static void ps3av_set_videomode_packet(u32 id)482{483 struct ps3av_pkt_avb_param avb_param;484 unsigned int i;485 u32 len = 0, av_video_cs;486 const struct avset_video_mode *video_mode;487 int res;488 489 video_mode = &video_mode_table[id & PS3AV_MODE_MASK];490 491 avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */492 avb_param.num_of_audio_pkt = 0;493 avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi +494 ps3av->av_hw_conf.num_of_avmulti;495 avb_param.num_of_av_audio_pkt = 0;496 497 /* video_pkt */498 for (i = 0; i < avb_param.num_of_video_pkt; i++)499 len += ps3av_cmd_set_video_mode(&avb_param.buf[len],500 ps3av->head[i], video_mode->vid,501 video_mode->fmt, id);502 /* av_video_pkt */503 for (i = 0; i < avb_param.num_of_av_video_pkt; i++) {504 if (id & PS3AV_MODE_DVI || id & PS3AV_MODE_RGB)505 av_video_cs = RGB8;506 else507 av_video_cs = video_mode->cs;508#ifndef PS3AV_HDMI_YUV509 if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 ||510 ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1)511 av_video_cs = RGB8; /* use RGB for HDMI */512#endif513 len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len],514 ps3av->av_port[i],515 video_mode->vid, av_video_cs,516 video_mode->aspect, id);517 }518 /* send command using avb pkt */519 len += offsetof(struct ps3av_pkt_avb_param, buf);520 res = ps3av_cmd_avb_param(&avb_param, len);521 if (res == PS3AV_STATUS_NO_SYNC_HEAD)522 printk(KERN_WARNING523 "%s: Command failed. Please try your request again.\n",524 __func__);525 else if (res)526 dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n");527}528 529static void ps3av_set_videomode_cont(u32 id, u32 old_id)530{531 static int vesa;532 int res;533 534 /* video signal off */535 ps3av_set_video_disable_sig();536 537 /*538 * AV backend needs non-VESA mode setting at least one time539 * when VESA mode is used.540 */541 if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) {542 /* vesa mode */543 ps3av_set_videomode_packet(PS3AV_MODE_480P);544 }545 vesa = 1;546 547 /* Retail PS3 product doesn't support this */548 if (id & PS3AV_MODE_HDCP_OFF) {549 res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF);550 if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE)551 dev_dbg(&ps3av->dev->core, "Not supported\n");552 else if (res)553 dev_dbg(&ps3av->dev->core,554 "ps3av_cmd_av_hdmi_mode failed\n");555 } else if (old_id & PS3AV_MODE_HDCP_OFF) {556 res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL);557 if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE)558 dev_dbg(&ps3av->dev->core,559 "ps3av_cmd_av_hdmi_mode failed\n");560 }561 562 ps3av_set_videomode_packet(id);563 564 msleep(1500);565 /* av video mute */566 ps3av_set_av_video_mute(PS3AV_CMD_MUTE_OFF);567}568 569static void ps3avd(struct work_struct *work)570{571 ps3av_set_videomode_cont(ps3av->ps3av_mode, ps3av->ps3av_mode_old);572 complete(&ps3av->done);573}574 575#define SHIFT_50 0576#define SHIFT_60 4577#define SHIFT_VESA 8578 579static const struct {580 unsigned mask:19;581 unsigned id:4;582} ps3av_preferred_modes[] = {583 { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA },584 { PS3AV_RESBIT_1920x1080P << SHIFT_60, PS3AV_MODE_1080P60 },585 { PS3AV_RESBIT_1920x1080P << SHIFT_50, PS3AV_MODE_1080P50 },586 { PS3AV_RESBIT_1920x1080I << SHIFT_60, PS3AV_MODE_1080I60 },587 { PS3AV_RESBIT_1920x1080I << SHIFT_50, PS3AV_MODE_1080I50 },588 { PS3AV_RESBIT_SXGA << SHIFT_VESA, PS3AV_MODE_SXGA },589 { PS3AV_RESBIT_WXGA << SHIFT_VESA, PS3AV_MODE_WXGA },590 { PS3AV_RESBIT_1280x720P << SHIFT_60, PS3AV_MODE_720P60 },591 { PS3AV_RESBIT_1280x720P << SHIFT_50, PS3AV_MODE_720P50 },592 { PS3AV_RESBIT_720x480P << SHIFT_60, PS3AV_MODE_480P },593 { PS3AV_RESBIT_720x576P << SHIFT_50, PS3AV_MODE_576P },594};595 596static enum ps3av_mode_num ps3av_resbit2id(u32 res_50, u32 res_60,597 u32 res_vesa)598{599 unsigned int i;600 u32 res_all;601 602 /*603 * We mask off the resolution bits we care about and combine the604 * results in one bitfield, so make sure there's no overlap605 */606 BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 &607 PS3AV_RES_MASK_60 << SHIFT_60);608 BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 &609 PS3AV_RES_MASK_VESA << SHIFT_VESA);610 BUILD_BUG_ON(PS3AV_RES_MASK_60 << SHIFT_60 &611 PS3AV_RES_MASK_VESA << SHIFT_VESA);612 res_all = (res_50 & PS3AV_RES_MASK_50) << SHIFT_50 |613 (res_60 & PS3AV_RES_MASK_60) << SHIFT_60 |614 (res_vesa & PS3AV_RES_MASK_VESA) << SHIFT_VESA;615 616 if (!res_all)617 return 0;618 619 for (i = 0; i < ARRAY_SIZE(ps3av_preferred_modes); i++)620 if (res_all & ps3av_preferred_modes[i].mask)621 return ps3av_preferred_modes[i].id;622 623 return 0;624}625 626static enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info)627{628 enum ps3av_mode_num id;629 630 if (safe_mode)631 return PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;632 633 /* check native resolution */634 id = ps3av_resbit2id(info->res_50.native, info->res_60.native,635 info->res_vesa.native);636 if (id) {637 pr_debug("%s: Using native mode %d\n", __func__, id);638 return id;639 }640 641 /* check supported resolutions */642 id = ps3av_resbit2id(info->res_50.res_bits, info->res_60.res_bits,643 info->res_vesa.res_bits);644 if (id) {645 pr_debug("%s: Using supported mode %d\n", __func__, id);646 return id;647 }648 649 if (ps3av->region & PS3AV_REGION_60)650 id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;651 else652 id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;653 pr_debug("%s: Using default mode %d\n", __func__, id);654 return id;655}656 657static void ps3av_monitor_info_dump(658 const struct ps3av_pkt_av_get_monitor_info *monitor_info)659{660 const struct ps3av_info_monitor *info = &monitor_info->info;661 const struct ps3av_info_audio *audio = info->audio;662 char id[sizeof(info->monitor_id)*3+1];663 int i;664 665 pr_debug("Monitor Info: size %u\n", monitor_info->send_hdr.size);666 667 pr_debug("avport: %02x\n", info->avport);668 for (i = 0; i < sizeof(info->monitor_id); i++)669 sprintf(&id[i*3], " %02x", info->monitor_id[i]);670 pr_debug("monitor_id: %s\n", id);671 pr_debug("monitor_type: %02x\n", info->monitor_type);672 pr_debug("monitor_name: %.*s\n", (int)sizeof(info->monitor_name),673 info->monitor_name);674 675 /* resolution */676 pr_debug("resolution_60: bits: %08x native: %08x\n",677 info->res_60.res_bits, info->res_60.native);678 pr_debug("resolution_50: bits: %08x native: %08x\n",679 info->res_50.res_bits, info->res_50.native);680 pr_debug("resolution_other: bits: %08x native: %08x\n",681 info->res_other.res_bits, info->res_other.native);682 pr_debug("resolution_vesa: bits: %08x native: %08x\n",683 info->res_vesa.res_bits, info->res_vesa.native);684 685 /* color space */686 pr_debug("color space rgb: %02x\n", info->cs.rgb);687 pr_debug("color space yuv444: %02x\n", info->cs.yuv444);688 pr_debug("color space yuv422: %02x\n", info->cs.yuv422);689 690 /* color info */691 pr_debug("color info red: X %04x Y %04x\n", info->color.red_x,692 info->color.red_y);693 pr_debug("color info green: X %04x Y %04x\n", info->color.green_x,694 info->color.green_y);695 pr_debug("color info blue: X %04x Y %04x\n", info->color.blue_x,696 info->color.blue_y);697 pr_debug("color info white: X %04x Y %04x\n", info->color.white_x,698 info->color.white_y);699 pr_debug("color info gamma: %08x\n", info->color.gamma);700 701 /* other info */702 pr_debug("supported_AI: %02x\n", info->supported_ai);703 pr_debug("speaker_info: %02x\n", info->speaker_info);704 pr_debug("num of audio: %02x\n", info->num_of_audio_block);705 706 /* audio block */707 for (i = 0; i < info->num_of_audio_block; i++) {708 pr_debug(709 "audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n",710 i, audio->type, audio->max_num_of_ch, audio->fs,711 audio->sbit);712 audio++;713 }714}715 716static const struct ps3av_monitor_quirk {717 const char *monitor_name;718 u32 clear_60;719} ps3av_monitor_quirks[] = {720 {721 .monitor_name = "DELL 2007WFP",722 .clear_60 = PS3AV_RESBIT_1920x1080I723 }, {724 .monitor_name = "L226WTQ",725 .clear_60 = PS3AV_RESBIT_1920x1080I |726 PS3AV_RESBIT_1920x1080P727 }, {728 .monitor_name = "SyncMaster",729 .clear_60 = PS3AV_RESBIT_1920x1080I730 }731};732 733static void ps3av_fixup_monitor_info(struct ps3av_info_monitor *info)734{735 unsigned int i;736 const struct ps3av_monitor_quirk *quirk;737 738 for (i = 0; i < ARRAY_SIZE(ps3av_monitor_quirks); i++) {739 quirk = &ps3av_monitor_quirks[i];740 if (!strncmp(info->monitor_name, quirk->monitor_name,741 sizeof(info->monitor_name))) {742 pr_info("%s: Applying quirk for %s\n", __func__,743 quirk->monitor_name);744 info->res_60.res_bits &= ~quirk->clear_60;745 info->res_60.native &= ~quirk->clear_60;746 break;747 }748 }749}750 751static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf)752{753 int i, res, id = 0, dvi = 0, rgb = 0;754 struct ps3av_pkt_av_get_monitor_info monitor_info;755 struct ps3av_info_monitor *info;756 757 /* get mode id for hdmi */758 for (i = 0; i < av_hw_conf->num_of_hdmi && !id; i++) {759 res = ps3av_cmd_video_get_monitor_info(&monitor_info,760 PS3AV_CMD_AVPORT_HDMI_0 +761 i);762 if (res < 0)763 return -1;764 765 ps3av_monitor_info_dump(&monitor_info);766 767 info = &monitor_info.info;768 ps3av_fixup_monitor_info(info);769 770 switch (info->monitor_type) {771 case PS3AV_MONITOR_TYPE_DVI:772 dvi = PS3AV_MODE_DVI;773 fallthrough;774 case PS3AV_MONITOR_TYPE_HDMI:775 id = ps3av_hdmi_get_id(info);776 break;777 }778 }779 780 if (!id) {781 /* no HDMI interface or HDMI is off */782 if (ps3av->region & PS3AV_REGION_60)783 id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_60;784 else785 id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_50;786 if (ps3av->region & PS3AV_REGION_RGB)787 rgb = PS3AV_MODE_RGB;788 pr_debug("%s: Using avmulti mode %d\n", __func__, id);789 }790 791 return id | dvi | rgb;792}793 794static int ps3av_get_hw_conf(struct ps3av *ps3av)795{796 int i, j, k, res;797 const struct ps3av_pkt_av_get_hw_conf *hw_conf;798 799 /* get av_hw_conf */800 res = ps3av_cmd_av_get_hw_conf(&ps3av->av_hw_conf);801 if (res < 0)802 return -1;803 804 hw_conf = &ps3av->av_hw_conf;805 pr_debug("av_h_conf: num of hdmi: %u\n", hw_conf->num_of_hdmi);806 pr_debug("av_h_conf: num of avmulti: %u\n", hw_conf->num_of_avmulti);807 pr_debug("av_h_conf: num of spdif: %u\n", hw_conf->num_of_spdif);808 809 for (i = 0; i < PS3AV_HEAD_MAX; i++)810 ps3av->head[i] = PS3AV_CMD_VIDEO_HEAD_A + i;811 for (i = 0; i < PS3AV_OPT_PORT_MAX; i++)812 ps3av->opt_port[i] = PS3AV_CMD_AVPORT_SPDIF_0 + i;813 for (i = 0; i < hw_conf->num_of_hdmi; i++)814 ps3av->av_port[i] = PS3AV_CMD_AVPORT_HDMI_0 + i;815 for (j = 0; j < hw_conf->num_of_avmulti; j++)816 ps3av->av_port[i + j] = PS3AV_CMD_AVPORT_AVMULTI_0 + j;817 for (k = 0; k < hw_conf->num_of_spdif; k++)818 ps3av->av_port[i + j + k] = PS3AV_CMD_AVPORT_SPDIF_0 + k;819 820 /* set all audio port */821 ps3av->audio_port = PS3AV_CMD_AUDIO_PORT_HDMI_0822 | PS3AV_CMD_AUDIO_PORT_HDMI_1823 | PS3AV_CMD_AUDIO_PORT_AVMULTI_0824 | PS3AV_CMD_AUDIO_PORT_SPDIF_0 | PS3AV_CMD_AUDIO_PORT_SPDIF_1;825 826 return 0;827}828 829/* set mode using id */830int ps3av_set_video_mode(int id)831{832 int size;833 u32 option;834 835 size = ARRAY_SIZE(video_mode_table);836 if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) {837 dev_dbg(&ps3av->dev->core, "%s: error id :%d\n", __func__, id);838 return -EINVAL;839 }840 841 /* auto mode */842 option = id & ~PS3AV_MODE_MASK;843 if ((id & PS3AV_MODE_MASK) == PS3AV_MODE_AUTO) {844 id = ps3av_auto_videomode(&ps3av->av_hw_conf);845 if (id < 1) {846 printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);847 return -EINVAL;848 }849 id |= option;850 }851 852 /* set videomode */853 wait_for_completion(&ps3av->done);854 ps3av->ps3av_mode_old = ps3av->ps3av_mode;855 ps3av->ps3av_mode = id;856 if (ps3av_set_videomode())857 ps3av->ps3av_mode = ps3av->ps3av_mode_old;858 859 return 0;860}861EXPORT_SYMBOL_GPL(ps3av_set_video_mode);862 863int ps3av_get_auto_mode(void)864{865 return ps3av_auto_videomode(&ps3av->av_hw_conf);866}867EXPORT_SYMBOL_GPL(ps3av_get_auto_mode);868 869int ps3av_get_mode(void)870{871 return ps3av ? ps3av->ps3av_mode : 0;872}873EXPORT_SYMBOL_GPL(ps3av_get_mode);874 875/* get resolution by video_mode */876int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres)877{878 int size;879 880 id = id & PS3AV_MODE_MASK;881 size = ARRAY_SIZE(video_mode_table);882 if (id > size - 1 || id < 0) {883 printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);884 return -EINVAL;885 }886 *xres = video_mode_table[id].x;887 *yres = video_mode_table[id].y;888 return 0;889}890EXPORT_SYMBOL_GPL(ps3av_video_mode2res);891 892/* mute */893int ps3av_video_mute(int mute)894{895 return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON896 : PS3AV_CMD_MUTE_OFF);897}898EXPORT_SYMBOL_GPL(ps3av_video_mute);899 900/* mute analog output only */901int ps3av_audio_mute_analog(int mute)902{903 int i, res;904 905 for (i = 0; i < ps3av->av_hw_conf.num_of_avmulti; i++) {906 res = ps3av_cmd_av_audio_mute(1,907 &ps3av->av_port[i + ps3av->av_hw_conf.num_of_hdmi],908 mute);909 if (res < 0)910 return -1;911 }912 return 0;913}914EXPORT_SYMBOL_GPL(ps3av_audio_mute_analog);915 916int ps3av_audio_mute(int mute)917{918 return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON919 : PS3AV_CMD_MUTE_OFF);920}921EXPORT_SYMBOL_GPL(ps3av_audio_mute);922 923static int ps3av_probe(struct ps3_system_bus_device *dev)924{925 const char *mode_option;926 int res;927 int id;928 929 dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);930 dev_dbg(&dev->core, " timeout=%d\n", timeout);931 932 if (ps3av) {933 dev_err(&dev->core, "Only one ps3av device is supported\n");934 return -EBUSY;935 }936 937 ps3av = kzalloc(sizeof(*ps3av), GFP_KERNEL);938 if (!ps3av)939 return -ENOMEM;940 941 mutex_init(&ps3av->mutex);942 ps3av->ps3av_mode = PS3AV_MODE_AUTO;943 ps3av->dev = dev;944 945 INIT_WORK(&ps3av->work, ps3avd);946 init_completion(&ps3av->done);947 complete(&ps3av->done);948 949 switch (ps3_os_area_get_av_multi_out()) {950 case PS3_PARAM_AV_MULTI_OUT_NTSC:951 ps3av->region = PS3AV_REGION_60;952 break;953 case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR:954 case PS3_PARAM_AV_MULTI_OUT_SECAM:955 ps3av->region = PS3AV_REGION_50;956 break;957 case PS3_PARAM_AV_MULTI_OUT_PAL_RGB:958 ps3av->region = PS3AV_REGION_50 | PS3AV_REGION_RGB;959 break;960 default:961 ps3av->region = PS3AV_REGION_60;962 break;963 }964 965 /* init avsetting modules */966 res = ps3av_cmd_init();967 if (res < 0)968 printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __func__,969 res);970 971 ps3av_get_hw_conf(ps3av);972 973 mode_option = video_get_options(NULL);974 if (mode_option && !strcmp(mode_option, "safe"))975 safe_mode = 1;976 id = ps3av_auto_videomode(&ps3av->av_hw_conf);977 if (id < 0) {978 printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);979 res = -EINVAL;980 goto fail;981 }982 983 safe_mode = 0;984 985 mutex_lock(&ps3av->mutex);986 ps3av->ps3av_mode = id;987 mutex_unlock(&ps3av->mutex);988 989 dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);990 991 return 0;992 993fail:994 kfree(ps3av);995 ps3av = NULL;996 return res;997}998 999static int ps3av_remove(struct ps3_system_bus_device *dev)1000{1001 dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);1002 if (ps3av) {1003 ps3av_cmd_fin();1004 flush_work(&ps3av->work);1005 kfree(ps3av);1006 ps3av = NULL;1007 }1008 1009 dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);1010 return 0;1011}1012 1013static void ps3av_shutdown(struct ps3_system_bus_device *dev)1014{1015 dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);1016 ps3av_remove(dev);1017 dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);1018}1019 1020static struct ps3_vuart_port_driver ps3av_driver = {1021 .core.match_id = PS3_MATCH_ID_AV_SETTINGS,1022 .core.core.name = "ps3_av",1023 .probe = ps3av_probe,1024 .remove = ps3av_remove,1025 .shutdown = ps3av_shutdown,1026};1027 1028static int __init ps3av_module_init(void)1029{1030 int error;1031 1032 if (!firmware_has_feature(FW_FEATURE_PS3_LV1))1033 return -ENODEV;1034 1035 pr_debug(" -> %s:%d\n", __func__, __LINE__);1036 1037 error = ps3_vuart_port_driver_register(&ps3av_driver);1038 if (error) {1039 printk(KERN_ERR1040 "%s: ps3_vuart_port_driver_register failed %d\n",1041 __func__, error);1042 return error;1043 }1044 1045 pr_debug(" <- %s:%d\n", __func__, __LINE__);1046 return error;1047}1048 1049static void __exit ps3av_module_exit(void)1050{1051 pr_debug(" -> %s:%d\n", __func__, __LINE__);1052 ps3_vuart_port_driver_unregister(&ps3av_driver);1053 pr_debug(" <- %s:%d\n", __func__, __LINE__);1054}1055 1056subsys_initcall(ps3av_module_init);1057module_exit(ps3av_module_exit);1058 1059MODULE_LICENSE("GPL v2");1060MODULE_DESCRIPTION("PS3 AV Settings Driver");1061MODULE_AUTHOR("Sony Computer Entertainment Inc.");1062MODULE_ALIAS(PS3_MODULE_ALIAS_AV_SETTINGS);1063