| 1 | |
| 2 | |
| 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
|
| 25 | #define PS3AV_BUF_SIZE 512
|
| 26 |
|
| 27 | static int safe_mode;
|
| 28 |
|
| 29 | static int timeout = 5000;
|
| 30 | module_param(timeout, int, 0644);
|
| 31 |
|
| 32 | static 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 |
|
| 54 | #define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8
|
| 55 | #define RGB8 PS3AV_CMD_VIDEO_CS_RGB_8
|
| 56 |
|
| 57 | #define XRGB PS3AV_CMD_VIDEO_FMT_X8R8G8B8
|
| 58 |
|
| 59 | #define A_N PS3AV_CMD_AV_ASPECT_4_3
|
| 60 | #define A_W PS3AV_CMD_AV_ASPECT_16_9
|
| 61 | static 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, },
|
| 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 |
|
| 86 | static u32 cmd_table[] = {
|
| 87 |
|
| 88 | PS3AV_CID_AV_INIT,
|
| 89 | PS3AV_CID_AV_FIN,
|
| 90 | PS3AV_CID_VIDEO_INIT,
|
| 91 | PS3AV_CID_AUDIO_INIT,
|
| 92 |
|
| 93 |
|
| 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 |
|
| 116 | PS3AV_CID_AV_GET_HW_CONF,
|
| 117 | PS3AV_CID_AV_GET_MONITOR_INFO,
|
| 118 |
|
| 119 |
|
| 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 | 0
|
| 128 | };
|
| 129 |
|
| 130 | #define PS3AV_EVENT_CMD_MASK 0x10000000
|
| 131 | #define PS3AV_EVENT_ID_MASK 0x0000ffff
|
| 132 | #define PS3AV_CID_MASK 0xffffffff
|
| 133 | #define PS3AV_REPLY_BIT 0x80000000
|
| 134 |
|
| 135 | #define ps3av_event_get_port_id(cid) ((cid >> 16) & 0xff)
|
| 136 |
|
| 137 | static 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 |
|
| 152 | static 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 | else
|
| 164 | printk(KERN_ERR
|
| 165 | "%s: failed event packet, cid:%08x size:%d\n",
|
| 166 | __func__, hdr->cid, hdr->size);
|
| 167 | return 1;
|
| 168 | }
|
| 169 | return 0;
|
| 170 | }
|
| 171 |
|
| 172 |
|
| 173 | #define POLLING_INTERVAL 25
|
| 174 |
|
| 175 | static 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 |
|
| 185 | static 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 |
|
| 207 | static 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 |
|
| 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 |
|
| 228 | cmd = send_buf->cid;
|
| 229 | do {
|
| 230 |
|
| 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 |
|
| 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;
|
| 250 | event = ps3av_parse_event_packet(recv_buf);
|
| 251 |
|
| 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 |
|
| 263 | static 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;
|
| 279 | }
|
| 280 |
|
| 281 | void 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 |
|
| 288 | int 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 |
|
| 305 | ps3av_set_hdr(cid, send_len, buf);
|
| 306 |
|
| 307 |
|
| 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_ERR
|
| 312 | "%s: ps3av_send_cmd_pkt() failed (result=%d)\n",
|
| 313 | __func__, res);
|
| 314 | goto err;
|
| 315 | }
|
| 316 |
|
| 317 |
|
| 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 |
|
| 329 | err:
|
| 330 | mutex_unlock(&ps3av->mutex);
|
| 331 | printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res);
|
| 332 | return res;
|
| 333 | }
|
| 334 |
|
| 335 | static 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 |
|
| 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 |
|
| 351 | static 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 |
|
| 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 |
|
| 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 |
|
| 385 | static 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 |
|
| 407 | int 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;
|
| 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 |
|
| 426 | ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON);
|
| 427 |
|
| 428 |
|
| 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 |
|
| 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 |
|
| 440 | len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len],
|
| 441 | ps3av->av_port[i],
|
| 442 | &audio_mode, vid);
|
| 443 | }
|
| 444 |
|
| 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 |
|
| 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 |
|
| 458 | ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF);
|
| 459 |
|
| 460 |
|
| 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 | }
|
| 468 | EXPORT_SYMBOL_GPL(ps3av_set_audio_mode);
|
| 469 |
|
| 470 | static int ps3av_set_videomode(void)
|
| 471 | {
|
| 472 |
|
| 473 | ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON);
|
| 474 |
|
| 475 |
|
| 476 | schedule_work(&ps3av->work);
|
| 477 |
|
| 478 | return 0;
|
| 479 | }
|
| 480 |
|
| 481 | static 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;
|
| 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 |
|
| 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 |
|
| 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 | else
|
| 507 | av_video_cs = video_mode->cs;
|
| 508 | #ifndef PS3AV_HDMI_YUV
|
| 509 | 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;
|
| 512 | #endif
|
| 513 | 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 |
|
| 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_WARNING
|
| 523 | "%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 |
|
| 529 | static void ps3av_set_videomode_cont(u32 id, u32 old_id)
|
| 530 | {
|
| 531 | static int vesa;
|
| 532 | int res;
|
| 533 |
|
| 534 |
|
| 535 | ps3av_set_video_disable_sig();
|
| 536 |
|
| 537 | |
| 538 | * AV backend needs non-VESA mode setting at least one time
|
| 539 | * when VESA mode is used.
|
| 540 |
|
| 541 | if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) {
|
| 542 |
|
| 543 | ps3av_set_videomode_packet(PS3AV_MODE_480P);
|
| 544 | }
|
| 545 | vesa = 1;
|
| 546 |
|
| 547 |
|
| 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 |
|
| 566 | ps3av_set_av_video_mute(PS3AV_CMD_MUTE_OFF);
|
| 567 | }
|
| 568 |
|
| 569 | static 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 0
|
| 576 | #define SHIFT_60 4
|
| 577 | #define SHIFT_VESA 8
|
| 578 |
|
| 579 | static 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 |
|
| 596 | static 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 the
|
| 604 | * results in one bitfield, so make sure there's no overlap
|
| 605 |
|
| 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 |
|
| 626 | static 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 |
|
| 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 |
|
| 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 | else
|
| 652 | id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;
|
| 653 | pr_debug("%s: Using default mode %d\n", __func__, id);
|
| 654 | return id;
|
| 655 | }
|
| 656 |
|
| 657 | static 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 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 |
|
| 716 | static 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_1920x1080I
|
| 723 | }, {
|
| 724 | .monitor_name = "L226WTQ",
|
| 725 | .clear_60 = PS3AV_RESBIT_1920x1080I |
|
| 726 | PS3AV_RESBIT_1920x1080P
|
| 727 | }, {
|
| 728 | .monitor_name = "SyncMaster",
|
| 729 | .clear_60 = PS3AV_RESBIT_1920x1080I
|
| 730 | }
|
| 731 | };
|
| 732 |
|
| 733 | static 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 |
|
| 751 | static 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 |
|
| 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 |
|
| 782 | if (ps3av->region & PS3AV_REGION_60)
|
| 783 | id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_60;
|
| 784 | else
|
| 785 | 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 |
|
| 794 | static 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 |
|
| 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 |
|
| 821 | ps3av->audio_port = PS3AV_CMD_AUDIO_PORT_HDMI_0
|
| 822 | | PS3AV_CMD_AUDIO_PORT_HDMI_1
|
| 823 | | PS3AV_CMD_AUDIO_PORT_AVMULTI_0
|
| 824 | | PS3AV_CMD_AUDIO_PORT_SPDIF_0 | PS3AV_CMD_AUDIO_PORT_SPDIF_1;
|
| 825 |
|
| 826 | return 0;
|
| 827 | }
|
| 828 |
|
| 829 |
|
| 830 | int 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 |
|
| 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 |
|
| 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 | }
|
| 861 | EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
|
| 862 |
|
| 863 | int ps3av_get_auto_mode(void)
|
| 864 | {
|
| 865 | return ps3av_auto_videomode(&ps3av->av_hw_conf);
|
| 866 | }
|
| 867 | EXPORT_SYMBOL_GPL(ps3av_get_auto_mode);
|
| 868 |
|
| 869 | int ps3av_get_mode(void)
|
| 870 | {
|
| 871 | return ps3av ? ps3av->ps3av_mode : 0;
|
| 872 | }
|
| 873 | EXPORT_SYMBOL_GPL(ps3av_get_mode);
|
| 874 |
|
| 875 |
|
| 876 | int 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 | }
|
| 890 | EXPORT_SYMBOL_GPL(ps3av_video_mode2res);
|
| 891 |
|
| 892 |
|
| 893 | int ps3av_video_mute(int mute)
|
| 894 | {
|
| 895 | return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON
|
| 896 | : PS3AV_CMD_MUTE_OFF);
|
| 897 | }
|
| 898 | EXPORT_SYMBOL_GPL(ps3av_video_mute);
|
| 899 |
|
| 900 |
|
| 901 | int 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 | }
|
| 914 | EXPORT_SYMBOL_GPL(ps3av_audio_mute_analog);
|
| 915 |
|
| 916 | int ps3av_audio_mute(int mute)
|
| 917 | {
|
| 918 | return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
|
| 919 | : PS3AV_CMD_MUTE_OFF);
|
| 920 | }
|
| 921 | EXPORT_SYMBOL_GPL(ps3av_audio_mute);
|
| 922 |
|
| 923 | static 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_obj(*ps3av);
|
| 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 |
|
| 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 |
|
| 993 | fail:
|
| 994 | kfree(ps3av);
|
| 995 | ps3av = NULL;
|
| 996 | return res;
|
| 997 | }
|
| 998 |
|
| 999 | static 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 |
|
| 1013 | static 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 |
|
| 1020 | static 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 |
|
| 1028 | static 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_ERR
|
| 1040 | "%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 |
|
| 1049 | static 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 |
|
| 1056 | subsys_initcall(ps3av_module_init);
|
| 1057 | module_exit(ps3av_module_exit);
|
| 1058 |
|
| 1059 | MODULE_LICENSE("GPL v2");
|
| 1060 | MODULE_DESCRIPTION("PS3 AV Settings Driver");
|
| 1061 | MODULE_AUTHOR("Sony Computer Entertainment Inc.");
|
| 1062 | MODULE_ALIAS(PS3_MODULE_ALIAS_AV_SETTINGS);
|