1873e65bcSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 211227fd1SGeert Uytterhoeven /* 313a5e30cSGeoff Levand * PS3 AV backend support. 411227fd1SGeert Uytterhoeven * 513a5e30cSGeoff Levand * Copyright (C) 2007 Sony Computer Entertainment Inc. 613a5e30cSGeoff Levand * Copyright 2007 Sony Corp. 711227fd1SGeert Uytterhoeven */ 811227fd1SGeert Uytterhoeven 913a5e30cSGeoff Levand #include <linux/kernel.h> 1011227fd1SGeert Uytterhoeven #include <linux/module.h> 1111227fd1SGeert Uytterhoeven #include <linux/delay.h> 1211227fd1SGeert Uytterhoeven #include <linux/notifier.h> 1311227fd1SGeert Uytterhoeven #include <linux/ioctl.h> 14d7dd91ffSGeert Uytterhoeven #include <linux/fb.h> 155a0e3ad6STejun Heo #include <linux/slab.h> 16ef596c69SGeert Uytterhoeven 17ef596c69SGeert Uytterhoeven #include <asm/firmware.h> 1811227fd1SGeert Uytterhoeven #include <asm/ps3av.h> 1911227fd1SGeert Uytterhoeven #include <asm/ps3.h> 2011227fd1SGeert Uytterhoeven 2111227fd1SGeert Uytterhoeven #include "vuart.h" 2211227fd1SGeert Uytterhoeven 2311227fd1SGeert Uytterhoeven #define BUFSIZE 4096 /* vuart buf size */ 2411227fd1SGeert Uytterhoeven #define PS3AV_BUF_SIZE 512 /* max packet size */ 2511227fd1SGeert Uytterhoeven 26d7dd91ffSGeert Uytterhoeven static int safe_mode; 27d7dd91ffSGeert Uytterhoeven 2811227fd1SGeert Uytterhoeven static int timeout = 5000; /* in msec ( 5 sec ) */ 2911227fd1SGeert Uytterhoeven module_param(timeout, int, 0644); 3011227fd1SGeert Uytterhoeven 31fffe52e8SGeert Uytterhoeven static struct ps3av { 32fffe52e8SGeert Uytterhoeven struct mutex mutex; 33fffe52e8SGeert Uytterhoeven struct work_struct work; 34fffe52e8SGeert Uytterhoeven struct completion done; 35fffe52e8SGeert Uytterhoeven int open_count; 3613a5e30cSGeoff Levand struct ps3_system_bus_device *dev; 37fffe52e8SGeert Uytterhoeven 38fffe52e8SGeert Uytterhoeven int region; 39fffe52e8SGeert Uytterhoeven struct ps3av_pkt_av_get_hw_conf av_hw_conf; 40fffe52e8SGeert Uytterhoeven u32 av_port[PS3AV_AV_PORT_MAX + PS3AV_OPT_PORT_MAX]; 41fffe52e8SGeert Uytterhoeven u32 opt_port[PS3AV_OPT_PORT_MAX]; 42fffe52e8SGeert Uytterhoeven u32 head[PS3AV_HEAD_MAX]; 43fffe52e8SGeert Uytterhoeven u32 audio_port; 44fffe52e8SGeert Uytterhoeven int ps3av_mode; 45fffe52e8SGeert Uytterhoeven int ps3av_mode_old; 4613a5e30cSGeoff Levand union { 4713a5e30cSGeoff Levand struct ps3av_reply_hdr reply_hdr; 4813a5e30cSGeoff Levand u8 raw[PS3AV_BUF_SIZE]; 4913a5e30cSGeoff Levand } recv_buf; 5013a5e30cSGeoff Levand } *ps3av; 5111227fd1SGeert Uytterhoeven 5211227fd1SGeert Uytterhoeven /* color space */ 5311227fd1SGeert Uytterhoeven #define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8 5411227fd1SGeert Uytterhoeven #define RGB8 PS3AV_CMD_VIDEO_CS_RGB_8 5511227fd1SGeert Uytterhoeven /* format */ 5611227fd1SGeert Uytterhoeven #define XRGB PS3AV_CMD_VIDEO_FMT_X8R8G8B8 5711227fd1SGeert Uytterhoeven /* aspect */ 5811227fd1SGeert Uytterhoeven #define A_N PS3AV_CMD_AV_ASPECT_4_3 5911227fd1SGeert Uytterhoeven #define A_W PS3AV_CMD_AV_ASPECT_16_9 6011227fd1SGeert Uytterhoeven static const struct avset_video_mode { 6111227fd1SGeert Uytterhoeven u32 cs; 6211227fd1SGeert Uytterhoeven u32 fmt; 6311227fd1SGeert Uytterhoeven u32 vid; 6411227fd1SGeert Uytterhoeven u32 aspect; 6511227fd1SGeert Uytterhoeven u32 x; 6611227fd1SGeert Uytterhoeven u32 y; 6711227fd1SGeert Uytterhoeven } video_mode_table[] = { 6811227fd1SGeert Uytterhoeven { 0, }, /* auto */ 693c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480}, 703c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480}, 717a4a89c3Ssebastian.blanes@gmail.com {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_W, 1280, 720}, 723c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080}, 733c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080}, 743c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576}, 753c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576}, 767a4a89c3Ssebastian.blanes@gmail.com {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_W, 1280, 720}, 773c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080}, 783c4f594aSGeert Uytterhoeven {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080}, 793c4f594aSGeert Uytterhoeven { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768}, 803c4f594aSGeert Uytterhoeven { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024}, 813c4f594aSGeert Uytterhoeven { RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200}, 8211227fd1SGeert Uytterhoeven }; 8311227fd1SGeert Uytterhoeven 8411227fd1SGeert Uytterhoeven /* supported CIDs */ 8511227fd1SGeert Uytterhoeven static u32 cmd_table[] = { 8611227fd1SGeert Uytterhoeven /* init */ 8711227fd1SGeert Uytterhoeven PS3AV_CID_AV_INIT, 8811227fd1SGeert Uytterhoeven PS3AV_CID_AV_FIN, 8911227fd1SGeert Uytterhoeven PS3AV_CID_VIDEO_INIT, 9011227fd1SGeert Uytterhoeven PS3AV_CID_AUDIO_INIT, 9111227fd1SGeert Uytterhoeven 9211227fd1SGeert Uytterhoeven /* set */ 9311227fd1SGeert Uytterhoeven PS3AV_CID_AV_ENABLE_EVENT, 9411227fd1SGeert Uytterhoeven PS3AV_CID_AV_DISABLE_EVENT, 9511227fd1SGeert Uytterhoeven 9611227fd1SGeert Uytterhoeven PS3AV_CID_AV_VIDEO_CS, 9711227fd1SGeert Uytterhoeven PS3AV_CID_AV_VIDEO_MUTE, 9811227fd1SGeert Uytterhoeven PS3AV_CID_AV_VIDEO_DISABLE_SIG, 9911227fd1SGeert Uytterhoeven PS3AV_CID_AV_AUDIO_PARAM, 10011227fd1SGeert Uytterhoeven PS3AV_CID_AV_AUDIO_MUTE, 10111227fd1SGeert Uytterhoeven PS3AV_CID_AV_HDMI_MODE, 10211227fd1SGeert Uytterhoeven PS3AV_CID_AV_TV_MUTE, 10311227fd1SGeert Uytterhoeven 10411227fd1SGeert Uytterhoeven PS3AV_CID_VIDEO_MODE, 10511227fd1SGeert Uytterhoeven PS3AV_CID_VIDEO_FORMAT, 10611227fd1SGeert Uytterhoeven PS3AV_CID_VIDEO_PITCH, 10711227fd1SGeert Uytterhoeven 10811227fd1SGeert Uytterhoeven PS3AV_CID_AUDIO_MODE, 10911227fd1SGeert Uytterhoeven PS3AV_CID_AUDIO_MUTE, 11011227fd1SGeert Uytterhoeven PS3AV_CID_AUDIO_ACTIVE, 11111227fd1SGeert Uytterhoeven PS3AV_CID_AUDIO_INACTIVE, 11211227fd1SGeert Uytterhoeven PS3AV_CID_AVB_PARAM, 11311227fd1SGeert Uytterhoeven 11411227fd1SGeert Uytterhoeven /* get */ 11511227fd1SGeert Uytterhoeven PS3AV_CID_AV_GET_HW_CONF, 11611227fd1SGeert Uytterhoeven PS3AV_CID_AV_GET_MONITOR_INFO, 11711227fd1SGeert Uytterhoeven 11811227fd1SGeert Uytterhoeven /* event */ 11911227fd1SGeert Uytterhoeven PS3AV_CID_EVENT_UNPLUGGED, 12011227fd1SGeert Uytterhoeven PS3AV_CID_EVENT_PLUGGED, 12111227fd1SGeert Uytterhoeven PS3AV_CID_EVENT_HDCP_DONE, 12211227fd1SGeert Uytterhoeven PS3AV_CID_EVENT_HDCP_FAIL, 12311227fd1SGeert Uytterhoeven PS3AV_CID_EVENT_HDCP_AUTH, 12411227fd1SGeert Uytterhoeven PS3AV_CID_EVENT_HDCP_ERROR, 12511227fd1SGeert Uytterhoeven 12611227fd1SGeert Uytterhoeven 0 12711227fd1SGeert Uytterhoeven }; 12811227fd1SGeert Uytterhoeven 12911227fd1SGeert Uytterhoeven #define PS3AV_EVENT_CMD_MASK 0x10000000 13011227fd1SGeert Uytterhoeven #define PS3AV_EVENT_ID_MASK 0x0000ffff 13111227fd1SGeert Uytterhoeven #define PS3AV_CID_MASK 0xffffffff 13211227fd1SGeert Uytterhoeven #define PS3AV_REPLY_BIT 0x80000000 13311227fd1SGeert Uytterhoeven 13411227fd1SGeert Uytterhoeven #define ps3av_event_get_port_id(cid) ((cid >> 16) & 0xff) 13511227fd1SGeert Uytterhoeven 13611227fd1SGeert Uytterhoeven static u32 *ps3av_search_cmd_table(u32 cid, u32 mask) 13711227fd1SGeert Uytterhoeven { 13811227fd1SGeert Uytterhoeven u32 *table; 13911227fd1SGeert Uytterhoeven int i; 14011227fd1SGeert Uytterhoeven 14111227fd1SGeert Uytterhoeven table = cmd_table; 14211227fd1SGeert Uytterhoeven for (i = 0;; table++, i++) { 14311227fd1SGeert Uytterhoeven if ((*table & mask) == (cid & mask)) 14411227fd1SGeert Uytterhoeven break; 14511227fd1SGeert Uytterhoeven if (*table == 0) 14611227fd1SGeert Uytterhoeven return NULL; 14711227fd1SGeert Uytterhoeven } 14811227fd1SGeert Uytterhoeven return table; 14911227fd1SGeert Uytterhoeven } 15011227fd1SGeert Uytterhoeven 15111227fd1SGeert Uytterhoeven static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr) 15211227fd1SGeert Uytterhoeven { 15311227fd1SGeert Uytterhoeven u32 *table; 15411227fd1SGeert Uytterhoeven 15511227fd1SGeert Uytterhoeven if (hdr->cid & PS3AV_EVENT_CMD_MASK) { 15611227fd1SGeert Uytterhoeven table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK); 15711227fd1SGeert Uytterhoeven if (table) 15813a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 15911227fd1SGeert Uytterhoeven "recv event packet cid:%08x port:0x%x size:%d\n", 16011227fd1SGeert Uytterhoeven hdr->cid, ps3av_event_get_port_id(hdr->cid), 16111227fd1SGeert Uytterhoeven hdr->size); 16211227fd1SGeert Uytterhoeven else 16311227fd1SGeert Uytterhoeven printk(KERN_ERR 16411227fd1SGeert Uytterhoeven "%s: failed event packet, cid:%08x size:%d\n", 165253f04e7SGeert Uytterhoeven __func__, hdr->cid, hdr->size); 16611227fd1SGeert Uytterhoeven return 1; /* receive event packet */ 16711227fd1SGeert Uytterhoeven } 16811227fd1SGeert Uytterhoeven return 0; 16911227fd1SGeert Uytterhoeven } 17011227fd1SGeert Uytterhoeven 17113a5e30cSGeoff Levand 17213a5e30cSGeoff Levand #define POLLING_INTERVAL 25 /* in msec */ 17313a5e30cSGeoff Levand 17413a5e30cSGeoff Levand static int ps3av_vuart_write(struct ps3_system_bus_device *dev, 17513a5e30cSGeoff Levand const void *buf, unsigned long size) 17613a5e30cSGeoff Levand { 17713a5e30cSGeoff Levand int error; 17813a5e30cSGeoff Levand dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 17913a5e30cSGeoff Levand error = ps3_vuart_write(dev, buf, size); 18013a5e30cSGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 18113a5e30cSGeoff Levand return error ? error : size; 18213a5e30cSGeoff Levand } 18313a5e30cSGeoff Levand 18413a5e30cSGeoff Levand static int ps3av_vuart_read(struct ps3_system_bus_device *dev, void *buf, 18513a5e30cSGeoff Levand unsigned long size, int timeout) 18613a5e30cSGeoff Levand { 18713a5e30cSGeoff Levand int error; 18813a5e30cSGeoff Levand int loopcnt = 0; 18913a5e30cSGeoff Levand 19013a5e30cSGeoff Levand dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 19113a5e30cSGeoff Levand timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL; 19213a5e30cSGeoff Levand while (loopcnt++ <= timeout) { 19313a5e30cSGeoff Levand error = ps3_vuart_read(dev, buf, size); 19413a5e30cSGeoff Levand if (!error) 19513a5e30cSGeoff Levand return size; 19613a5e30cSGeoff Levand if (error != -EAGAIN) { 19713a5e30cSGeoff Levand printk(KERN_ERR "%s: ps3_vuart_read failed %d\n", 19813a5e30cSGeoff Levand __func__, error); 19913a5e30cSGeoff Levand return error; 20013a5e30cSGeoff Levand } 20113a5e30cSGeoff Levand msleep(POLLING_INTERVAL); 20213a5e30cSGeoff Levand } 20313a5e30cSGeoff Levand return -EWOULDBLOCK; 20413a5e30cSGeoff Levand } 20513a5e30cSGeoff Levand 20611227fd1SGeert Uytterhoeven static int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf, 20711227fd1SGeert Uytterhoeven struct ps3av_reply_hdr *recv_buf, int write_len, 20811227fd1SGeert Uytterhoeven int read_len) 20911227fd1SGeert Uytterhoeven { 21011227fd1SGeert Uytterhoeven int res; 21111227fd1SGeert Uytterhoeven u32 cmd; 21211227fd1SGeert Uytterhoeven int event; 21311227fd1SGeert Uytterhoeven 21413a5e30cSGeoff Levand if (!ps3av) 21511227fd1SGeert Uytterhoeven return -ENODEV; 21611227fd1SGeert Uytterhoeven 21711227fd1SGeert Uytterhoeven /* send pkt */ 21813a5e30cSGeoff Levand res = ps3av_vuart_write(ps3av->dev, send_buf, write_len); 21911227fd1SGeert Uytterhoeven if (res < 0) { 22013a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 22111227fd1SGeert Uytterhoeven "%s: ps3av_vuart_write() failed (result=%d)\n", 222253f04e7SGeert Uytterhoeven __func__, res); 22311227fd1SGeert Uytterhoeven return res; 22411227fd1SGeert Uytterhoeven } 22511227fd1SGeert Uytterhoeven 22611227fd1SGeert Uytterhoeven /* recv pkt */ 22711227fd1SGeert Uytterhoeven cmd = send_buf->cid; 22811227fd1SGeert Uytterhoeven do { 22911227fd1SGeert Uytterhoeven /* read header */ 23013a5e30cSGeoff Levand res = ps3av_vuart_read(ps3av->dev, recv_buf, PS3AV_HDR_SIZE, 23111227fd1SGeert Uytterhoeven timeout); 23211227fd1SGeert Uytterhoeven if (res != PS3AV_HDR_SIZE) { 23313a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 23411227fd1SGeert Uytterhoeven "%s: ps3av_vuart_read() failed (result=%d)\n", 235253f04e7SGeert Uytterhoeven __func__, res); 23611227fd1SGeert Uytterhoeven return res; 23711227fd1SGeert Uytterhoeven } 23811227fd1SGeert Uytterhoeven 23911227fd1SGeert Uytterhoeven /* read body */ 24013a5e30cSGeoff Levand res = ps3av_vuart_read(ps3av->dev, &recv_buf->cid, 24111227fd1SGeert Uytterhoeven recv_buf->size, timeout); 24211227fd1SGeert Uytterhoeven if (res < 0) { 24313a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 24411227fd1SGeert Uytterhoeven "%s: ps3av_vuart_read() failed (result=%d)\n", 245253f04e7SGeert Uytterhoeven __func__, res); 24611227fd1SGeert Uytterhoeven return res; 24711227fd1SGeert Uytterhoeven } 24811227fd1SGeert Uytterhoeven res += PS3AV_HDR_SIZE; /* total len */ 24911227fd1SGeert Uytterhoeven event = ps3av_parse_event_packet(recv_buf); 25011227fd1SGeert Uytterhoeven /* ret > 0 event packet */ 25111227fd1SGeert Uytterhoeven } while (event); 25211227fd1SGeert Uytterhoeven 25311227fd1SGeert Uytterhoeven if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) { 25413a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, "%s: reply err (result=%x)\n", 255253f04e7SGeert Uytterhoeven __func__, recv_buf->cid); 25611227fd1SGeert Uytterhoeven return -EINVAL; 25711227fd1SGeert Uytterhoeven } 25811227fd1SGeert Uytterhoeven 25911227fd1SGeert Uytterhoeven return 0; 26011227fd1SGeert Uytterhoeven } 26111227fd1SGeert Uytterhoeven 26211227fd1SGeert Uytterhoeven static int ps3av_process_reply_packet(struct ps3av_send_hdr *cmd_buf, 26311227fd1SGeert Uytterhoeven const struct ps3av_reply_hdr *recv_buf, 26411227fd1SGeert Uytterhoeven int user_buf_size) 26511227fd1SGeert Uytterhoeven { 26611227fd1SGeert Uytterhoeven int return_len; 26711227fd1SGeert Uytterhoeven 26811227fd1SGeert Uytterhoeven if (recv_buf->version != PS3AV_VERSION) { 26913a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, "reply_packet invalid version:%x\n", 27011227fd1SGeert Uytterhoeven recv_buf->version); 27111227fd1SGeert Uytterhoeven return -EFAULT; 27211227fd1SGeert Uytterhoeven } 27311227fd1SGeert Uytterhoeven return_len = recv_buf->size + PS3AV_HDR_SIZE; 27411227fd1SGeert Uytterhoeven if (return_len > user_buf_size) 27511227fd1SGeert Uytterhoeven return_len = user_buf_size; 27611227fd1SGeert Uytterhoeven memcpy(cmd_buf, recv_buf, return_len); 27711227fd1SGeert Uytterhoeven return 0; /* success */ 27811227fd1SGeert Uytterhoeven } 27911227fd1SGeert Uytterhoeven 28011227fd1SGeert Uytterhoeven void ps3av_set_hdr(u32 cid, u16 size, struct ps3av_send_hdr *hdr) 28111227fd1SGeert Uytterhoeven { 28211227fd1SGeert Uytterhoeven hdr->version = PS3AV_VERSION; 28311227fd1SGeert Uytterhoeven hdr->size = size - PS3AV_HDR_SIZE; 28411227fd1SGeert Uytterhoeven hdr->cid = cid; 28511227fd1SGeert Uytterhoeven } 28611227fd1SGeert Uytterhoeven 28711227fd1SGeert Uytterhoeven int ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size, 28811227fd1SGeert Uytterhoeven struct ps3av_send_hdr *buf) 28911227fd1SGeert Uytterhoeven { 29011227fd1SGeert Uytterhoeven int res = 0; 29111227fd1SGeert Uytterhoeven u32 *table; 29211227fd1SGeert Uytterhoeven 29313a5e30cSGeoff Levand BUG_ON(!ps3av); 29411227fd1SGeert Uytterhoeven 29513a5e30cSGeoff Levand mutex_lock(&ps3av->mutex); 29611227fd1SGeert Uytterhoeven 29711227fd1SGeert Uytterhoeven table = ps3av_search_cmd_table(cid, PS3AV_CID_MASK); 29811227fd1SGeert Uytterhoeven BUG_ON(!table); 29911227fd1SGeert Uytterhoeven BUG_ON(send_len < PS3AV_HDR_SIZE); 30011227fd1SGeert Uytterhoeven BUG_ON(usr_buf_size < send_len); 30111227fd1SGeert Uytterhoeven BUG_ON(usr_buf_size > PS3AV_BUF_SIZE); 30211227fd1SGeert Uytterhoeven 30311227fd1SGeert Uytterhoeven /* create header */ 30411227fd1SGeert Uytterhoeven ps3av_set_hdr(cid, send_len, buf); 30511227fd1SGeert Uytterhoeven 30611227fd1SGeert Uytterhoeven /* send packet via vuart */ 30713a5e30cSGeoff Levand res = ps3av_send_cmd_pkt(buf, &ps3av->recv_buf.reply_hdr, send_len, 30811227fd1SGeert Uytterhoeven usr_buf_size); 30911227fd1SGeert Uytterhoeven if (res < 0) { 31011227fd1SGeert Uytterhoeven printk(KERN_ERR 31111227fd1SGeert Uytterhoeven "%s: ps3av_send_cmd_pkt() failed (result=%d)\n", 312253f04e7SGeert Uytterhoeven __func__, res); 31311227fd1SGeert Uytterhoeven goto err; 31411227fd1SGeert Uytterhoeven } 31511227fd1SGeert Uytterhoeven 31611227fd1SGeert Uytterhoeven /* process reply packet */ 31713a5e30cSGeoff Levand res = ps3av_process_reply_packet(buf, &ps3av->recv_buf.reply_hdr, 31811227fd1SGeert Uytterhoeven usr_buf_size); 31911227fd1SGeert Uytterhoeven if (res < 0) { 32011227fd1SGeert Uytterhoeven printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n", 321253f04e7SGeert Uytterhoeven __func__, res); 32211227fd1SGeert Uytterhoeven goto err; 32311227fd1SGeert Uytterhoeven } 32411227fd1SGeert Uytterhoeven 32513a5e30cSGeoff Levand mutex_unlock(&ps3av->mutex); 32611227fd1SGeert Uytterhoeven return 0; 32711227fd1SGeert Uytterhoeven 32811227fd1SGeert Uytterhoeven err: 32913a5e30cSGeoff Levand mutex_unlock(&ps3av->mutex); 330253f04e7SGeert Uytterhoeven printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res); 33111227fd1SGeert Uytterhoeven return res; 33211227fd1SGeert Uytterhoeven } 33311227fd1SGeert Uytterhoeven 33411227fd1SGeert Uytterhoeven static int ps3av_set_av_video_mute(u32 mute) 33511227fd1SGeert Uytterhoeven { 33611227fd1SGeert Uytterhoeven int i, num_of_av_port, res; 33711227fd1SGeert Uytterhoeven 33813a5e30cSGeoff Levand num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + 33913a5e30cSGeoff Levand ps3av->av_hw_conf.num_of_avmulti; 34011227fd1SGeert Uytterhoeven /* video mute on */ 34111227fd1SGeert Uytterhoeven for (i = 0; i < num_of_av_port; i++) { 34213a5e30cSGeoff Levand res = ps3av_cmd_av_video_mute(1, &ps3av->av_port[i], mute); 34311227fd1SGeert Uytterhoeven if (res < 0) 34411227fd1SGeert Uytterhoeven return -1; 34511227fd1SGeert Uytterhoeven } 34611227fd1SGeert Uytterhoeven 34711227fd1SGeert Uytterhoeven return 0; 34811227fd1SGeert Uytterhoeven } 34911227fd1SGeert Uytterhoeven 35011227fd1SGeert Uytterhoeven static int ps3av_set_video_disable_sig(void) 35111227fd1SGeert Uytterhoeven { 35211227fd1SGeert Uytterhoeven int i, num_of_hdmi_port, num_of_av_port, res; 35311227fd1SGeert Uytterhoeven 35413a5e30cSGeoff Levand num_of_hdmi_port = ps3av->av_hw_conf.num_of_hdmi; 35513a5e30cSGeoff Levand num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + 35613a5e30cSGeoff Levand ps3av->av_hw_conf.num_of_avmulti; 35711227fd1SGeert Uytterhoeven 35811227fd1SGeert Uytterhoeven /* tv mute */ 35911227fd1SGeert Uytterhoeven for (i = 0; i < num_of_hdmi_port; i++) { 36013a5e30cSGeoff Levand res = ps3av_cmd_av_tv_mute(ps3av->av_port[i], 36111227fd1SGeert Uytterhoeven PS3AV_CMD_MUTE_ON); 36211227fd1SGeert Uytterhoeven if (res < 0) 36311227fd1SGeert Uytterhoeven return -1; 36411227fd1SGeert Uytterhoeven } 36511227fd1SGeert Uytterhoeven msleep(100); 36611227fd1SGeert Uytterhoeven 36711227fd1SGeert Uytterhoeven /* video mute on */ 36811227fd1SGeert Uytterhoeven for (i = 0; i < num_of_av_port; i++) { 36913a5e30cSGeoff Levand res = ps3av_cmd_av_video_disable_sig(ps3av->av_port[i]); 37011227fd1SGeert Uytterhoeven if (res < 0) 37111227fd1SGeert Uytterhoeven return -1; 37211227fd1SGeert Uytterhoeven if (i < num_of_hdmi_port) { 37313a5e30cSGeoff Levand res = ps3av_cmd_av_tv_mute(ps3av->av_port[i], 37411227fd1SGeert Uytterhoeven PS3AV_CMD_MUTE_OFF); 37511227fd1SGeert Uytterhoeven if (res < 0) 37611227fd1SGeert Uytterhoeven return -1; 37711227fd1SGeert Uytterhoeven } 37811227fd1SGeert Uytterhoeven } 37911227fd1SGeert Uytterhoeven msleep(300); 38011227fd1SGeert Uytterhoeven 38111227fd1SGeert Uytterhoeven return 0; 38211227fd1SGeert Uytterhoeven } 38311227fd1SGeert Uytterhoeven 38411227fd1SGeert Uytterhoeven static int ps3av_set_audio_mute(u32 mute) 38511227fd1SGeert Uytterhoeven { 38611227fd1SGeert Uytterhoeven int i, num_of_av_port, num_of_opt_port, res; 38711227fd1SGeert Uytterhoeven 38813a5e30cSGeoff Levand num_of_av_port = ps3av->av_hw_conf.num_of_hdmi + 38913a5e30cSGeoff Levand ps3av->av_hw_conf.num_of_avmulti; 39013a5e30cSGeoff Levand num_of_opt_port = ps3av->av_hw_conf.num_of_spdif; 39111227fd1SGeert Uytterhoeven 39211227fd1SGeert Uytterhoeven for (i = 0; i < num_of_av_port; i++) { 39313a5e30cSGeoff Levand res = ps3av_cmd_av_audio_mute(1, &ps3av->av_port[i], mute); 39411227fd1SGeert Uytterhoeven if (res < 0) 39511227fd1SGeert Uytterhoeven return -1; 39611227fd1SGeert Uytterhoeven } 39711227fd1SGeert Uytterhoeven for (i = 0; i < num_of_opt_port; i++) { 39813a5e30cSGeoff Levand res = ps3av_cmd_audio_mute(1, &ps3av->opt_port[i], mute); 39911227fd1SGeert Uytterhoeven if (res < 0) 40011227fd1SGeert Uytterhoeven return -1; 40111227fd1SGeert Uytterhoeven } 40211227fd1SGeert Uytterhoeven 40311227fd1SGeert Uytterhoeven return 0; 40411227fd1SGeert Uytterhoeven } 40511227fd1SGeert Uytterhoeven 40611227fd1SGeert Uytterhoeven int ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source) 40711227fd1SGeert Uytterhoeven { 40811227fd1SGeert Uytterhoeven struct ps3av_pkt_avb_param avb_param; 40911227fd1SGeert Uytterhoeven int i, num_of_audio, vid, res; 41011227fd1SGeert Uytterhoeven struct ps3av_pkt_audio_mode audio_mode; 41111227fd1SGeert Uytterhoeven u32 len = 0; 41211227fd1SGeert Uytterhoeven 41313a5e30cSGeoff Levand num_of_audio = ps3av->av_hw_conf.num_of_hdmi + 41413a5e30cSGeoff Levand ps3av->av_hw_conf.num_of_avmulti + 41513a5e30cSGeoff Levand ps3av->av_hw_conf.num_of_spdif; 41611227fd1SGeert Uytterhoeven 41711227fd1SGeert Uytterhoeven avb_param.num_of_video_pkt = 0; 41811227fd1SGeert Uytterhoeven avb_param.num_of_audio_pkt = PS3AV_AVB_NUM_AUDIO; /* always 0 */ 41911227fd1SGeert Uytterhoeven avb_param.num_of_av_video_pkt = 0; 42013a5e30cSGeoff Levand avb_param.num_of_av_audio_pkt = ps3av->av_hw_conf.num_of_hdmi; 42111227fd1SGeert Uytterhoeven 42213a5e30cSGeoff Levand vid = video_mode_table[ps3av->ps3av_mode].vid; 42311227fd1SGeert Uytterhoeven 42411227fd1SGeert Uytterhoeven /* audio mute */ 42511227fd1SGeert Uytterhoeven ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON); 42611227fd1SGeert Uytterhoeven 42711227fd1SGeert Uytterhoeven /* audio inactive */ 42813a5e30cSGeoff Levand res = ps3av_cmd_audio_active(0, ps3av->audio_port); 42911227fd1SGeert Uytterhoeven if (res < 0) 43013a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 43111227fd1SGeert Uytterhoeven "ps3av_cmd_audio_active OFF failed\n"); 43211227fd1SGeert Uytterhoeven 43311227fd1SGeert Uytterhoeven /* audio_pkt */ 43411227fd1SGeert Uytterhoeven for (i = 0; i < num_of_audio; i++) { 43513a5e30cSGeoff Levand ps3av_cmd_set_audio_mode(&audio_mode, ps3av->av_port[i], ch, 43613a5e30cSGeoff Levand fs, word_bits, format, source); 43713a5e30cSGeoff Levand if (i < ps3av->av_hw_conf.num_of_hdmi) { 43811227fd1SGeert Uytterhoeven /* hdmi only */ 43911227fd1SGeert Uytterhoeven len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len], 44013a5e30cSGeoff Levand ps3av->av_port[i], 44111227fd1SGeert Uytterhoeven &audio_mode, vid); 44211227fd1SGeert Uytterhoeven } 44311227fd1SGeert Uytterhoeven /* audio_mode pkt should be sent separately */ 44411227fd1SGeert Uytterhoeven res = ps3av_cmd_audio_mode(&audio_mode); 44511227fd1SGeert Uytterhoeven if (res < 0) 44613a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 44711227fd1SGeert Uytterhoeven "ps3av_cmd_audio_mode failed, port:%x\n", i); 44811227fd1SGeert Uytterhoeven } 44911227fd1SGeert Uytterhoeven 45011227fd1SGeert Uytterhoeven /* send command using avb pkt */ 45111227fd1SGeert Uytterhoeven len += offsetof(struct ps3av_pkt_avb_param, buf); 45211227fd1SGeert Uytterhoeven res = ps3av_cmd_avb_param(&avb_param, len); 45311227fd1SGeert Uytterhoeven if (res < 0) 45413a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); 45511227fd1SGeert Uytterhoeven 45611227fd1SGeert Uytterhoeven /* audio mute */ 45711227fd1SGeert Uytterhoeven ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF); 45811227fd1SGeert Uytterhoeven 45911227fd1SGeert Uytterhoeven /* audio active */ 46013a5e30cSGeoff Levand res = ps3av_cmd_audio_active(1, ps3av->audio_port); 46111227fd1SGeert Uytterhoeven if (res < 0) 46213a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, 46313a5e30cSGeoff Levand "ps3av_cmd_audio_active ON failed\n"); 46411227fd1SGeert Uytterhoeven 46511227fd1SGeert Uytterhoeven return 0; 46611227fd1SGeert Uytterhoeven } 46711227fd1SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3av_set_audio_mode); 46811227fd1SGeert Uytterhoeven 46911227fd1SGeert Uytterhoeven static int ps3av_set_videomode(void) 47011227fd1SGeert Uytterhoeven { 47111227fd1SGeert Uytterhoeven /* av video mute */ 47211227fd1SGeert Uytterhoeven ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON); 47311227fd1SGeert Uytterhoeven 47411227fd1SGeert Uytterhoeven /* wake up ps3avd to do the actual video mode setting */ 475f0200c02SBhaktipriya Shridhar schedule_work(&ps3av->work); 47611227fd1SGeert Uytterhoeven 47711227fd1SGeert Uytterhoeven return 0; 47811227fd1SGeert Uytterhoeven } 47911227fd1SGeert Uytterhoeven 4808ca0bf75SMasashi Kimoto static void ps3av_set_videomode_packet(u32 id) 48111227fd1SGeert Uytterhoeven { 48211227fd1SGeert Uytterhoeven struct ps3av_pkt_avb_param avb_param; 4838ca0bf75SMasashi Kimoto unsigned int i; 48411227fd1SGeert Uytterhoeven u32 len = 0, av_video_cs; 48511227fd1SGeert Uytterhoeven const struct avset_video_mode *video_mode; 48611227fd1SGeert Uytterhoeven int res; 48711227fd1SGeert Uytterhoeven 48811227fd1SGeert Uytterhoeven video_mode = &video_mode_table[id & PS3AV_MODE_MASK]; 48911227fd1SGeert Uytterhoeven 49011227fd1SGeert Uytterhoeven avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */ 49111227fd1SGeert Uytterhoeven avb_param.num_of_audio_pkt = 0; 49213a5e30cSGeoff Levand avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi + 49313a5e30cSGeoff Levand ps3av->av_hw_conf.num_of_avmulti; 49411227fd1SGeert Uytterhoeven avb_param.num_of_av_audio_pkt = 0; 49511227fd1SGeert Uytterhoeven 49611227fd1SGeert Uytterhoeven /* video_pkt */ 49711227fd1SGeert Uytterhoeven for (i = 0; i < avb_param.num_of_video_pkt; i++) 49811227fd1SGeert Uytterhoeven len += ps3av_cmd_set_video_mode(&avb_param.buf[len], 49913a5e30cSGeoff Levand ps3av->head[i], video_mode->vid, 50011227fd1SGeert Uytterhoeven video_mode->fmt, id); 50111227fd1SGeert Uytterhoeven /* av_video_pkt */ 50211227fd1SGeert Uytterhoeven for (i = 0; i < avb_param.num_of_av_video_pkt; i++) { 50311227fd1SGeert Uytterhoeven if (id & PS3AV_MODE_DVI || id & PS3AV_MODE_RGB) 50411227fd1SGeert Uytterhoeven av_video_cs = RGB8; 50511227fd1SGeert Uytterhoeven else 50611227fd1SGeert Uytterhoeven av_video_cs = video_mode->cs; 50711227fd1SGeert Uytterhoeven #ifndef PS3AV_HDMI_YUV 50813a5e30cSGeoff Levand if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 || 50913a5e30cSGeoff Levand ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1) 51011227fd1SGeert Uytterhoeven av_video_cs = RGB8; /* use RGB for HDMI */ 51111227fd1SGeert Uytterhoeven #endif 51211227fd1SGeert Uytterhoeven len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len], 51313a5e30cSGeoff Levand ps3av->av_port[i], 51411227fd1SGeert Uytterhoeven video_mode->vid, av_video_cs, 51511227fd1SGeert Uytterhoeven video_mode->aspect, id); 51611227fd1SGeert Uytterhoeven } 51711227fd1SGeert Uytterhoeven /* send command using avb pkt */ 51811227fd1SGeert Uytterhoeven len += offsetof(struct ps3av_pkt_avb_param, buf); 51911227fd1SGeert Uytterhoeven res = ps3av_cmd_avb_param(&avb_param, len); 52011227fd1SGeert Uytterhoeven if (res == PS3AV_STATUS_NO_SYNC_HEAD) 52111227fd1SGeert Uytterhoeven printk(KERN_WARNING 52211227fd1SGeert Uytterhoeven "%s: Command failed. Please try your request again.\n", 523253f04e7SGeert Uytterhoeven __func__); 52411227fd1SGeert Uytterhoeven else if (res) 52513a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n"); 5268ca0bf75SMasashi Kimoto } 5278ca0bf75SMasashi Kimoto 5288ca0bf75SMasashi Kimoto static void ps3av_set_videomode_cont(u32 id, u32 old_id) 5298ca0bf75SMasashi Kimoto { 530084ffff2SGeert Uytterhoeven static int vesa; 5318ca0bf75SMasashi Kimoto int res; 5328ca0bf75SMasashi Kimoto 5338ca0bf75SMasashi Kimoto /* video signal off */ 5348ca0bf75SMasashi Kimoto ps3av_set_video_disable_sig(); 5358ca0bf75SMasashi Kimoto 5368ca0bf75SMasashi Kimoto /* 5378ca0bf75SMasashi Kimoto * AV backend needs non-VESA mode setting at least one time 5388ca0bf75SMasashi Kimoto * when VESA mode is used. 5398ca0bf75SMasashi Kimoto */ 540084ffff2SGeert Uytterhoeven if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) { 5418ca0bf75SMasashi Kimoto /* vesa mode */ 542084ffff2SGeert Uytterhoeven ps3av_set_videomode_packet(PS3AV_MODE_480P); 5438ca0bf75SMasashi Kimoto } 5448ca0bf75SMasashi Kimoto vesa = 1; 5458ca0bf75SMasashi Kimoto 5468ca0bf75SMasashi Kimoto /* Retail PS3 product doesn't support this */ 5478ca0bf75SMasashi Kimoto if (id & PS3AV_MODE_HDCP_OFF) { 5488ca0bf75SMasashi Kimoto res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF); 5498ca0bf75SMasashi Kimoto if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) 5508ca0bf75SMasashi Kimoto dev_dbg(&ps3av->dev->core, "Not supported\n"); 5518ca0bf75SMasashi Kimoto else if (res) 5528ca0bf75SMasashi Kimoto dev_dbg(&ps3av->dev->core, 5538ca0bf75SMasashi Kimoto "ps3av_cmd_av_hdmi_mode failed\n"); 5548ca0bf75SMasashi Kimoto } else if (old_id & PS3AV_MODE_HDCP_OFF) { 5558ca0bf75SMasashi Kimoto res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL); 5568ca0bf75SMasashi Kimoto if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE) 5578ca0bf75SMasashi Kimoto dev_dbg(&ps3av->dev->core, 5588ca0bf75SMasashi Kimoto "ps3av_cmd_av_hdmi_mode failed\n"); 5598ca0bf75SMasashi Kimoto } 5608ca0bf75SMasashi Kimoto 5618ca0bf75SMasashi Kimoto ps3av_set_videomode_packet(id); 56211227fd1SGeert Uytterhoeven 56311227fd1SGeert Uytterhoeven msleep(1500); 56411227fd1SGeert Uytterhoeven /* av video mute */ 56511227fd1SGeert Uytterhoeven ps3av_set_av_video_mute(PS3AV_CMD_MUTE_OFF); 56611227fd1SGeert Uytterhoeven } 56711227fd1SGeert Uytterhoeven 5685caf5db8SGeert Uytterhoeven static void ps3avd(struct work_struct *work) 56911227fd1SGeert Uytterhoeven { 57013a5e30cSGeoff Levand ps3av_set_videomode_cont(ps3av->ps3av_mode, ps3av->ps3av_mode_old); 57113a5e30cSGeoff Levand complete(&ps3av->done); 57211227fd1SGeert Uytterhoeven } 57311227fd1SGeert Uytterhoeven 574fd562112SGeert Uytterhoeven #define SHIFT_50 0 575fd562112SGeert Uytterhoeven #define SHIFT_60 4 576fd562112SGeert Uytterhoeven #define SHIFT_VESA 8 57711227fd1SGeert Uytterhoeven 578fd562112SGeert Uytterhoeven static const struct { 579fd562112SGeert Uytterhoeven unsigned mask:19; 580fd562112SGeert Uytterhoeven unsigned id:4; 581fd562112SGeert Uytterhoeven } ps3av_preferred_modes[] = { 582084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_WUXGA << SHIFT_VESA, PS3AV_MODE_WUXGA }, 583084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_1920x1080P << SHIFT_60, PS3AV_MODE_1080P60 }, 584084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_1920x1080P << SHIFT_50, PS3AV_MODE_1080P50 }, 585084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_1920x1080I << SHIFT_60, PS3AV_MODE_1080I60 }, 586084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_1920x1080I << SHIFT_50, PS3AV_MODE_1080I50 }, 587084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_SXGA << SHIFT_VESA, PS3AV_MODE_SXGA }, 588084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_WXGA << SHIFT_VESA, PS3AV_MODE_WXGA }, 589084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_1280x720P << SHIFT_60, PS3AV_MODE_720P60 }, 590084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_1280x720P << SHIFT_50, PS3AV_MODE_720P50 }, 591084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_720x480P << SHIFT_60, PS3AV_MODE_480P }, 592084ffff2SGeert Uytterhoeven { PS3AV_RESBIT_720x576P << SHIFT_50, PS3AV_MODE_576P }, 593fd562112SGeert Uytterhoeven }; 594fd562112SGeert Uytterhoeven 595084ffff2SGeert Uytterhoeven static enum ps3av_mode_num ps3av_resbit2id(u32 res_50, u32 res_60, 596084ffff2SGeert Uytterhoeven u32 res_vesa) 597fd562112SGeert Uytterhoeven { 598fd562112SGeert Uytterhoeven unsigned int i; 599fd562112SGeert Uytterhoeven u32 res_all; 600fd562112SGeert Uytterhoeven 601fd562112SGeert Uytterhoeven /* 602fd562112SGeert Uytterhoeven * We mask off the resolution bits we care about and combine the 603fd562112SGeert Uytterhoeven * results in one bitfield, so make sure there's no overlap 604fd562112SGeert Uytterhoeven */ 605fd562112SGeert Uytterhoeven BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 & 606fd562112SGeert Uytterhoeven PS3AV_RES_MASK_60 << SHIFT_60); 607fd562112SGeert Uytterhoeven BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 & 608fd562112SGeert Uytterhoeven PS3AV_RES_MASK_VESA << SHIFT_VESA); 609fd562112SGeert Uytterhoeven BUILD_BUG_ON(PS3AV_RES_MASK_60 << SHIFT_60 & 610fd562112SGeert Uytterhoeven PS3AV_RES_MASK_VESA << SHIFT_VESA); 611fd562112SGeert Uytterhoeven res_all = (res_50 & PS3AV_RES_MASK_50) << SHIFT_50 | 612fd562112SGeert Uytterhoeven (res_60 & PS3AV_RES_MASK_60) << SHIFT_60 | 613fd562112SGeert Uytterhoeven (res_vesa & PS3AV_RES_MASK_VESA) << SHIFT_VESA; 614fd562112SGeert Uytterhoeven 615fd562112SGeert Uytterhoeven if (!res_all) 616fd562112SGeert Uytterhoeven return 0; 617fd562112SGeert Uytterhoeven 618fd562112SGeert Uytterhoeven for (i = 0; i < ARRAY_SIZE(ps3av_preferred_modes); i++) 619fd562112SGeert Uytterhoeven if (res_all & ps3av_preferred_modes[i].mask) 620fd562112SGeert Uytterhoeven return ps3av_preferred_modes[i].id; 621fd562112SGeert Uytterhoeven 622fd562112SGeert Uytterhoeven return 0; 62311227fd1SGeert Uytterhoeven } 62411227fd1SGeert Uytterhoeven 625084ffff2SGeert Uytterhoeven static enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info) 62611227fd1SGeert Uytterhoeven { 627084ffff2SGeert Uytterhoeven enum ps3av_mode_num id; 62811227fd1SGeert Uytterhoeven 629d7dd91ffSGeert Uytterhoeven if (safe_mode) 630d7dd91ffSGeert Uytterhoeven return PS3AV_DEFAULT_HDMI_MODE_ID_REG_60; 631d7dd91ffSGeert Uytterhoeven 63211227fd1SGeert Uytterhoeven /* check native resolution */ 633fd562112SGeert Uytterhoeven id = ps3av_resbit2id(info->res_50.native, info->res_60.native, 634fd562112SGeert Uytterhoeven info->res_vesa.native); 635fd562112SGeert Uytterhoeven if (id) { 636fd562112SGeert Uytterhoeven pr_debug("%s: Using native mode %d\n", __func__, id); 63771a27fecSGeert Uytterhoeven return id; 63811227fd1SGeert Uytterhoeven } 63911227fd1SGeert Uytterhoeven 640fd562112SGeert Uytterhoeven /* check supported resolutions */ 641fd562112SGeert Uytterhoeven id = ps3av_resbit2id(info->res_50.res_bits, info->res_60.res_bits, 642fd562112SGeert Uytterhoeven info->res_vesa.res_bits); 643fd562112SGeert Uytterhoeven if (id) { 644fd562112SGeert Uytterhoeven pr_debug("%s: Using supported mode %d\n", __func__, id); 64571a27fecSGeert Uytterhoeven return id; 64611227fd1SGeert Uytterhoeven } 64711227fd1SGeert Uytterhoeven 64813a5e30cSGeoff Levand if (ps3av->region & PS3AV_REGION_60) 64971a27fecSGeert Uytterhoeven id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60; 65011227fd1SGeert Uytterhoeven else 65171a27fecSGeert Uytterhoeven id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50; 652fd562112SGeert Uytterhoeven pr_debug("%s: Using default mode %d\n", __func__, id); 65371a27fecSGeert Uytterhoeven return id; 65411227fd1SGeert Uytterhoeven } 65511227fd1SGeert Uytterhoeven 656f02777b4SValentin Ilie static void ps3av_monitor_info_dump( 657f02777b4SValentin Ilie const struct ps3av_pkt_av_get_monitor_info *monitor_info) 658eea820abSGeert Uytterhoeven { 659eea820abSGeert Uytterhoeven const struct ps3av_info_monitor *info = &monitor_info->info; 660eea820abSGeert Uytterhoeven const struct ps3av_info_audio *audio = info->audio; 661eea820abSGeert Uytterhoeven char id[sizeof(info->monitor_id)*3+1]; 662eea820abSGeert Uytterhoeven int i; 663eea820abSGeert Uytterhoeven 664eea820abSGeert Uytterhoeven pr_debug("Monitor Info: size %u\n", monitor_info->send_hdr.size); 665eea820abSGeert Uytterhoeven 666eea820abSGeert Uytterhoeven pr_debug("avport: %02x\n", info->avport); 667eea820abSGeert Uytterhoeven for (i = 0; i < sizeof(info->monitor_id); i++) 668eea820abSGeert Uytterhoeven sprintf(&id[i*3], " %02x", info->monitor_id[i]); 669eea820abSGeert Uytterhoeven pr_debug("monitor_id: %s\n", id); 670eea820abSGeert Uytterhoeven pr_debug("monitor_type: %02x\n", info->monitor_type); 671eea820abSGeert Uytterhoeven pr_debug("monitor_name: %.*s\n", (int)sizeof(info->monitor_name), 672eea820abSGeert Uytterhoeven info->monitor_name); 673eea820abSGeert Uytterhoeven 674eea820abSGeert Uytterhoeven /* resolution */ 675eea820abSGeert Uytterhoeven pr_debug("resolution_60: bits: %08x native: %08x\n", 676eea820abSGeert Uytterhoeven info->res_60.res_bits, info->res_60.native); 677eea820abSGeert Uytterhoeven pr_debug("resolution_50: bits: %08x native: %08x\n", 678eea820abSGeert Uytterhoeven info->res_50.res_bits, info->res_50.native); 679eea820abSGeert Uytterhoeven pr_debug("resolution_other: bits: %08x native: %08x\n", 680eea820abSGeert Uytterhoeven info->res_other.res_bits, info->res_other.native); 681eea820abSGeert Uytterhoeven pr_debug("resolution_vesa: bits: %08x native: %08x\n", 682eea820abSGeert Uytterhoeven info->res_vesa.res_bits, info->res_vesa.native); 683eea820abSGeert Uytterhoeven 684eea820abSGeert Uytterhoeven /* color space */ 685eea820abSGeert Uytterhoeven pr_debug("color space rgb: %02x\n", info->cs.rgb); 686eea820abSGeert Uytterhoeven pr_debug("color space yuv444: %02x\n", info->cs.yuv444); 687eea820abSGeert Uytterhoeven pr_debug("color space yuv422: %02x\n", info->cs.yuv422); 688eea820abSGeert Uytterhoeven 689eea820abSGeert Uytterhoeven /* color info */ 690eea820abSGeert Uytterhoeven pr_debug("color info red: X %04x Y %04x\n", info->color.red_x, 691eea820abSGeert Uytterhoeven info->color.red_y); 692eea820abSGeert Uytterhoeven pr_debug("color info green: X %04x Y %04x\n", info->color.green_x, 693eea820abSGeert Uytterhoeven info->color.green_y); 694eea820abSGeert Uytterhoeven pr_debug("color info blue: X %04x Y %04x\n", info->color.blue_x, 695eea820abSGeert Uytterhoeven info->color.blue_y); 696eea820abSGeert Uytterhoeven pr_debug("color info white: X %04x Y %04x\n", info->color.white_x, 697eea820abSGeert Uytterhoeven info->color.white_y); 698eea820abSGeert Uytterhoeven pr_debug("color info gamma: %08x\n", info->color.gamma); 699eea820abSGeert Uytterhoeven 700eea820abSGeert Uytterhoeven /* other info */ 701eea820abSGeert Uytterhoeven pr_debug("supported_AI: %02x\n", info->supported_ai); 702eea820abSGeert Uytterhoeven pr_debug("speaker_info: %02x\n", info->speaker_info); 703eea820abSGeert Uytterhoeven pr_debug("num of audio: %02x\n", info->num_of_audio_block); 704eea820abSGeert Uytterhoeven 705eea820abSGeert Uytterhoeven /* audio block */ 706eea820abSGeert Uytterhoeven for (i = 0; i < info->num_of_audio_block; i++) { 707f02777b4SValentin Ilie pr_debug( 708f02777b4SValentin Ilie "audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n", 709eea820abSGeert Uytterhoeven i, audio->type, audio->max_num_of_ch, audio->fs, 710eea820abSGeert Uytterhoeven audio->sbit); 711eea820abSGeert Uytterhoeven audio++; 712eea820abSGeert Uytterhoeven } 713eea820abSGeert Uytterhoeven } 714eea820abSGeert Uytterhoeven 71557f70c60SGeert Uytterhoeven static const struct ps3av_monitor_quirk { 71657f70c60SGeert Uytterhoeven const char *monitor_name; 7173305a6bcSGeert Uytterhoeven u32 clear_60; 71857f70c60SGeert Uytterhoeven } ps3av_monitor_quirks[] = { 71957f70c60SGeert Uytterhoeven { 72057f70c60SGeert Uytterhoeven .monitor_name = "DELL 2007WFP", 72157f70c60SGeert Uytterhoeven .clear_60 = PS3AV_RESBIT_1920x1080I 72257f70c60SGeert Uytterhoeven }, { 72357f70c60SGeert Uytterhoeven .monitor_name = "L226WTQ", 72457f70c60SGeert Uytterhoeven .clear_60 = PS3AV_RESBIT_1920x1080I | 72557f70c60SGeert Uytterhoeven PS3AV_RESBIT_1920x1080P 72657f70c60SGeert Uytterhoeven }, { 72757f70c60SGeert Uytterhoeven .monitor_name = "SyncMaster", 72857f70c60SGeert Uytterhoeven .clear_60 = PS3AV_RESBIT_1920x1080I 72957f70c60SGeert Uytterhoeven } 73057f70c60SGeert Uytterhoeven }; 73157f70c60SGeert Uytterhoeven 73257f70c60SGeert Uytterhoeven static void ps3av_fixup_monitor_info(struct ps3av_info_monitor *info) 73357f70c60SGeert Uytterhoeven { 73457f70c60SGeert Uytterhoeven unsigned int i; 73557f70c60SGeert Uytterhoeven const struct ps3av_monitor_quirk *quirk; 73657f70c60SGeert Uytterhoeven 73757f70c60SGeert Uytterhoeven for (i = 0; i < ARRAY_SIZE(ps3av_monitor_quirks); i++) { 73857f70c60SGeert Uytterhoeven quirk = &ps3av_monitor_quirks[i]; 73957f70c60SGeert Uytterhoeven if (!strncmp(info->monitor_name, quirk->monitor_name, 74057f70c60SGeert Uytterhoeven sizeof(info->monitor_name))) { 74157f70c60SGeert Uytterhoeven pr_info("%s: Applying quirk for %s\n", __func__, 74257f70c60SGeert Uytterhoeven quirk->monitor_name); 74357f70c60SGeert Uytterhoeven info->res_60.res_bits &= ~quirk->clear_60; 74457f70c60SGeert Uytterhoeven info->res_60.native &= ~quirk->clear_60; 74557f70c60SGeert Uytterhoeven break; 74657f70c60SGeert Uytterhoeven } 74757f70c60SGeert Uytterhoeven } 74857f70c60SGeert Uytterhoeven } 74957f70c60SGeert Uytterhoeven 750ce4c371aSGeert Uytterhoeven static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf) 75111227fd1SGeert Uytterhoeven { 75271a27fecSGeert Uytterhoeven int i, res, id = 0, dvi = 0, rgb = 0; 75311227fd1SGeert Uytterhoeven struct ps3av_pkt_av_get_monitor_info monitor_info; 75411227fd1SGeert Uytterhoeven struct ps3av_info_monitor *info; 75511227fd1SGeert Uytterhoeven 75671a27fecSGeert Uytterhoeven /* get mode id for hdmi */ 757101aa56dSGeert Uytterhoeven for (i = 0; i < av_hw_conf->num_of_hdmi && !id; i++) { 75811227fd1SGeert Uytterhoeven res = ps3av_cmd_video_get_monitor_info(&monitor_info, 75911227fd1SGeert Uytterhoeven PS3AV_CMD_AVPORT_HDMI_0 + 76011227fd1SGeert Uytterhoeven i); 76111227fd1SGeert Uytterhoeven if (res < 0) 76211227fd1SGeert Uytterhoeven return -1; 76311227fd1SGeert Uytterhoeven 764eea820abSGeert Uytterhoeven ps3av_monitor_info_dump(&monitor_info); 765101aa56dSGeert Uytterhoeven 76611227fd1SGeert Uytterhoeven info = &monitor_info.info; 76757f70c60SGeert Uytterhoeven ps3av_fixup_monitor_info(info); 76857f70c60SGeert Uytterhoeven 769101aa56dSGeert Uytterhoeven switch (info->monitor_type) { 770101aa56dSGeert Uytterhoeven case PS3AV_MONITOR_TYPE_DVI: 77111227fd1SGeert Uytterhoeven dvi = PS3AV_MODE_DVI; 772101aa56dSGeert Uytterhoeven /* fall through */ 773101aa56dSGeert Uytterhoeven case PS3AV_MONITOR_TYPE_HDMI: 77471a27fecSGeert Uytterhoeven id = ps3av_hdmi_get_id(info); 77511227fd1SGeert Uytterhoeven break; 77611227fd1SGeert Uytterhoeven } 77711227fd1SGeert Uytterhoeven } 77811227fd1SGeert Uytterhoeven 779101aa56dSGeert Uytterhoeven if (!id) { 78011227fd1SGeert Uytterhoeven /* no HDMI interface or HDMI is off */ 78113a5e30cSGeoff Levand if (ps3av->region & PS3AV_REGION_60) 78271a27fecSGeert Uytterhoeven id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_60; 78311227fd1SGeert Uytterhoeven else 78471a27fecSGeert Uytterhoeven id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_50; 78513a5e30cSGeoff Levand if (ps3av->region & PS3AV_REGION_RGB) 78611227fd1SGeert Uytterhoeven rgb = PS3AV_MODE_RGB; 787fd562112SGeert Uytterhoeven pr_debug("%s: Using avmulti mode %d\n", __func__, id); 78811227fd1SGeert Uytterhoeven } 78911227fd1SGeert Uytterhoeven 79071a27fecSGeert Uytterhoeven return id | dvi | rgb; 79111227fd1SGeert Uytterhoeven } 79211227fd1SGeert Uytterhoeven 79311227fd1SGeert Uytterhoeven static int ps3av_get_hw_conf(struct ps3av *ps3av) 79411227fd1SGeert Uytterhoeven { 79511227fd1SGeert Uytterhoeven int i, j, k, res; 796eea820abSGeert Uytterhoeven const struct ps3av_pkt_av_get_hw_conf *hw_conf; 79711227fd1SGeert Uytterhoeven 79811227fd1SGeert Uytterhoeven /* get av_hw_conf */ 79911227fd1SGeert Uytterhoeven res = ps3av_cmd_av_get_hw_conf(&ps3av->av_hw_conf); 80011227fd1SGeert Uytterhoeven if (res < 0) 80111227fd1SGeert Uytterhoeven return -1; 80211227fd1SGeert Uytterhoeven 803eea820abSGeert Uytterhoeven hw_conf = &ps3av->av_hw_conf; 804eea820abSGeert Uytterhoeven pr_debug("av_h_conf: num of hdmi: %u\n", hw_conf->num_of_hdmi); 805eea820abSGeert Uytterhoeven pr_debug("av_h_conf: num of avmulti: %u\n", hw_conf->num_of_avmulti); 806eea820abSGeert Uytterhoeven pr_debug("av_h_conf: num of spdif: %u\n", hw_conf->num_of_spdif); 80711227fd1SGeert Uytterhoeven 80811227fd1SGeert Uytterhoeven for (i = 0; i < PS3AV_HEAD_MAX; i++) 80911227fd1SGeert Uytterhoeven ps3av->head[i] = PS3AV_CMD_VIDEO_HEAD_A + i; 81011227fd1SGeert Uytterhoeven for (i = 0; i < PS3AV_OPT_PORT_MAX; i++) 81111227fd1SGeert Uytterhoeven ps3av->opt_port[i] = PS3AV_CMD_AVPORT_SPDIF_0 + i; 812eea820abSGeert Uytterhoeven for (i = 0; i < hw_conf->num_of_hdmi; i++) 81311227fd1SGeert Uytterhoeven ps3av->av_port[i] = PS3AV_CMD_AVPORT_HDMI_0 + i; 814eea820abSGeert Uytterhoeven for (j = 0; j < hw_conf->num_of_avmulti; j++) 81511227fd1SGeert Uytterhoeven ps3av->av_port[i + j] = PS3AV_CMD_AVPORT_AVMULTI_0 + j; 816eea820abSGeert Uytterhoeven for (k = 0; k < hw_conf->num_of_spdif; k++) 81711227fd1SGeert Uytterhoeven ps3av->av_port[i + j + k] = PS3AV_CMD_AVPORT_SPDIF_0 + k; 81811227fd1SGeert Uytterhoeven 81911227fd1SGeert Uytterhoeven /* set all audio port */ 82011227fd1SGeert Uytterhoeven ps3av->audio_port = PS3AV_CMD_AUDIO_PORT_HDMI_0 82111227fd1SGeert Uytterhoeven | PS3AV_CMD_AUDIO_PORT_HDMI_1 82211227fd1SGeert Uytterhoeven | PS3AV_CMD_AUDIO_PORT_AVMULTI_0 82311227fd1SGeert Uytterhoeven | PS3AV_CMD_AUDIO_PORT_SPDIF_0 | PS3AV_CMD_AUDIO_PORT_SPDIF_1; 82411227fd1SGeert Uytterhoeven 82511227fd1SGeert Uytterhoeven return 0; 82611227fd1SGeert Uytterhoeven } 82711227fd1SGeert Uytterhoeven 82811227fd1SGeert Uytterhoeven /* set mode using id */ 829e7eec2fcSroel kluin int ps3av_set_video_mode(int id) 83011227fd1SGeert Uytterhoeven { 83111227fd1SGeert Uytterhoeven int size; 83211227fd1SGeert Uytterhoeven u32 option; 83311227fd1SGeert Uytterhoeven 83411227fd1SGeert Uytterhoeven size = ARRAY_SIZE(video_mode_table); 83511227fd1SGeert Uytterhoeven if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) { 83613a5e30cSGeoff Levand dev_dbg(&ps3av->dev->core, "%s: error id :%d\n", __func__, id); 83711227fd1SGeert Uytterhoeven return -EINVAL; 83811227fd1SGeert Uytterhoeven } 83911227fd1SGeert Uytterhoeven 84011227fd1SGeert Uytterhoeven /* auto mode */ 84111227fd1SGeert Uytterhoeven option = id & ~PS3AV_MODE_MASK; 842084ffff2SGeert Uytterhoeven if ((id & PS3AV_MODE_MASK) == PS3AV_MODE_AUTO) { 843ce4c371aSGeert Uytterhoeven id = ps3av_auto_videomode(&ps3av->av_hw_conf); 84411227fd1SGeert Uytterhoeven if (id < 1) { 845253f04e7SGeert Uytterhoeven printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); 84611227fd1SGeert Uytterhoeven return -EINVAL; 84711227fd1SGeert Uytterhoeven } 84811227fd1SGeert Uytterhoeven id |= option; 84911227fd1SGeert Uytterhoeven } 85011227fd1SGeert Uytterhoeven 85111227fd1SGeert Uytterhoeven /* set videomode */ 85213a5e30cSGeoff Levand wait_for_completion(&ps3av->done); 85313a5e30cSGeoff Levand ps3av->ps3av_mode_old = ps3av->ps3av_mode; 85413a5e30cSGeoff Levand ps3av->ps3av_mode = id; 85511227fd1SGeert Uytterhoeven if (ps3av_set_videomode()) 85613a5e30cSGeoff Levand ps3av->ps3av_mode = ps3av->ps3av_mode_old; 85711227fd1SGeert Uytterhoeven 85811227fd1SGeert Uytterhoeven return 0; 85911227fd1SGeert Uytterhoeven } 86011227fd1SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3av_set_video_mode); 86111227fd1SGeert Uytterhoeven 862ce4c371aSGeert Uytterhoeven int ps3av_get_auto_mode(void) 86364072901SMasashi Kimoto { 864ce4c371aSGeert Uytterhoeven return ps3av_auto_videomode(&ps3av->av_hw_conf); 86564072901SMasashi Kimoto } 86664072901SMasashi Kimoto EXPORT_SYMBOL_GPL(ps3av_get_auto_mode); 86764072901SMasashi Kimoto 86811227fd1SGeert Uytterhoeven int ps3av_get_mode(void) 86911227fd1SGeert Uytterhoeven { 87013a5e30cSGeoff Levand return ps3av ? ps3av->ps3av_mode : 0; 87111227fd1SGeert Uytterhoeven } 87211227fd1SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3av_get_mode); 87311227fd1SGeert Uytterhoeven 87411227fd1SGeert Uytterhoeven /* get resolution by video_mode */ 87511227fd1SGeert Uytterhoeven int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres) 87611227fd1SGeert Uytterhoeven { 87711227fd1SGeert Uytterhoeven int size; 87811227fd1SGeert Uytterhoeven 87911227fd1SGeert Uytterhoeven id = id & PS3AV_MODE_MASK; 88011227fd1SGeert Uytterhoeven size = ARRAY_SIZE(video_mode_table); 88111227fd1SGeert Uytterhoeven if (id > size - 1 || id < 0) { 882253f04e7SGeert Uytterhoeven printk(KERN_ERR "%s: invalid mode %d\n", __func__, id); 88311227fd1SGeert Uytterhoeven return -EINVAL; 88411227fd1SGeert Uytterhoeven } 88511227fd1SGeert Uytterhoeven *xres = video_mode_table[id].x; 88611227fd1SGeert Uytterhoeven *yres = video_mode_table[id].y; 88711227fd1SGeert Uytterhoeven return 0; 88811227fd1SGeert Uytterhoeven } 88911227fd1SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3av_video_mode2res); 89011227fd1SGeert Uytterhoeven 89111227fd1SGeert Uytterhoeven /* mute */ 89211227fd1SGeert Uytterhoeven int ps3av_video_mute(int mute) 89311227fd1SGeert Uytterhoeven { 89411227fd1SGeert Uytterhoeven return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON 89511227fd1SGeert Uytterhoeven : PS3AV_CMD_MUTE_OFF); 89611227fd1SGeert Uytterhoeven } 89711227fd1SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3av_video_mute); 89811227fd1SGeert Uytterhoeven 899756ba83eSMasakazu Mokuno /* mute analog output only */ 900756ba83eSMasakazu Mokuno int ps3av_audio_mute_analog(int mute) 901756ba83eSMasakazu Mokuno { 902756ba83eSMasakazu Mokuno int i, res; 903756ba83eSMasakazu Mokuno 904756ba83eSMasakazu Mokuno for (i = 0; i < ps3av->av_hw_conf.num_of_avmulti; i++) { 905756ba83eSMasakazu Mokuno res = ps3av_cmd_av_audio_mute(1, 906756ba83eSMasakazu Mokuno &ps3av->av_port[i + ps3av->av_hw_conf.num_of_hdmi], 907756ba83eSMasakazu Mokuno mute); 908756ba83eSMasakazu Mokuno if (res < 0) 909756ba83eSMasakazu Mokuno return -1; 910756ba83eSMasakazu Mokuno } 911756ba83eSMasakazu Mokuno return 0; 912756ba83eSMasakazu Mokuno } 913756ba83eSMasakazu Mokuno EXPORT_SYMBOL_GPL(ps3av_audio_mute_analog); 914756ba83eSMasakazu Mokuno 91511227fd1SGeert Uytterhoeven int ps3av_audio_mute(int mute) 91611227fd1SGeert Uytterhoeven { 91711227fd1SGeert Uytterhoeven return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON 91811227fd1SGeert Uytterhoeven : PS3AV_CMD_MUTE_OFF); 91911227fd1SGeert Uytterhoeven } 92011227fd1SGeert Uytterhoeven EXPORT_SYMBOL_GPL(ps3av_audio_mute); 92111227fd1SGeert Uytterhoeven 9220fe763c5SGreg Kroah-Hartman static int ps3av_probe(struct ps3_system_bus_device *dev) 92311227fd1SGeert Uytterhoeven { 92411227fd1SGeert Uytterhoeven int res; 925e7eec2fcSroel kluin int id; 92611227fd1SGeert Uytterhoeven 92713a5e30cSGeoff Levand dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 92813a5e30cSGeoff Levand dev_dbg(&dev->core, " timeout=%d\n", timeout); 92911227fd1SGeert Uytterhoeven 93013a5e30cSGeoff Levand if (ps3av) { 93113a5e30cSGeoff Levand dev_err(&dev->core, "Only one ps3av device is supported\n"); 93213a5e30cSGeoff Levand return -EBUSY; 93313a5e30cSGeoff Levand } 93411227fd1SGeert Uytterhoeven 93513a5e30cSGeoff Levand ps3av = kzalloc(sizeof(*ps3av), GFP_KERNEL); 93613a5e30cSGeoff Levand if (!ps3av) 9375caf5db8SGeert Uytterhoeven return -ENOMEM; 93811227fd1SGeert Uytterhoeven 93913a5e30cSGeoff Levand mutex_init(&ps3av->mutex); 940084ffff2SGeert Uytterhoeven ps3av->ps3av_mode = PS3AV_MODE_AUTO; 94113a5e30cSGeoff Levand ps3av->dev = dev; 94213a5e30cSGeoff Levand 94313a5e30cSGeoff Levand INIT_WORK(&ps3av->work, ps3avd); 94413a5e30cSGeoff Levand init_completion(&ps3av->done); 94513a5e30cSGeoff Levand complete(&ps3av->done); 94613a5e30cSGeoff Levand 94711227fd1SGeert Uytterhoeven switch (ps3_os_area_get_av_multi_out()) { 94811227fd1SGeert Uytterhoeven case PS3_PARAM_AV_MULTI_OUT_NTSC: 94913a5e30cSGeoff Levand ps3av->region = PS3AV_REGION_60; 95011227fd1SGeert Uytterhoeven break; 95111227fd1SGeert Uytterhoeven case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR: 95211227fd1SGeert Uytterhoeven case PS3_PARAM_AV_MULTI_OUT_SECAM: 95313a5e30cSGeoff Levand ps3av->region = PS3AV_REGION_50; 95411227fd1SGeert Uytterhoeven break; 95511227fd1SGeert Uytterhoeven case PS3_PARAM_AV_MULTI_OUT_PAL_RGB: 95613a5e30cSGeoff Levand ps3av->region = PS3AV_REGION_50 | PS3AV_REGION_RGB; 95711227fd1SGeert Uytterhoeven break; 95811227fd1SGeert Uytterhoeven default: 95913a5e30cSGeoff Levand ps3av->region = PS3AV_REGION_60; 96011227fd1SGeert Uytterhoeven break; 96111227fd1SGeert Uytterhoeven } 96211227fd1SGeert Uytterhoeven 96311227fd1SGeert Uytterhoeven /* init avsetting modules */ 96411227fd1SGeert Uytterhoeven res = ps3av_cmd_init(); 96511227fd1SGeert Uytterhoeven if (res < 0) 966253f04e7SGeert Uytterhoeven printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __func__, 96711227fd1SGeert Uytterhoeven res); 96811227fd1SGeert Uytterhoeven 96913a5e30cSGeoff Levand ps3av_get_hw_conf(ps3av); 970d7dd91ffSGeert Uytterhoeven 971d7dd91ffSGeert Uytterhoeven #ifdef CONFIG_FB 972d7dd91ffSGeert Uytterhoeven if (fb_mode_option && !strcmp(fb_mode_option, "safe")) 973d7dd91ffSGeert Uytterhoeven safe_mode = 1; 974d7dd91ffSGeert Uytterhoeven #endif /* CONFIG_FB */ 975ce4c371aSGeert Uytterhoeven id = ps3av_auto_videomode(&ps3av->av_hw_conf); 976e7eec2fcSroel kluin if (id < 0) { 977e7eec2fcSroel kluin printk(KERN_ERR "%s: invalid id :%d\n", __func__, id); 978e7eec2fcSroel kluin res = -EINVAL; 979e7eec2fcSroel kluin goto fail; 980e7eec2fcSroel kluin } 981e7eec2fcSroel kluin 982d7dd91ffSGeert Uytterhoeven safe_mode = 0; 983d7dd91ffSGeert Uytterhoeven 98413a5e30cSGeoff Levand mutex_lock(&ps3av->mutex); 98513a5e30cSGeoff Levand ps3av->ps3av_mode = id; 98613a5e30cSGeoff Levand mutex_unlock(&ps3av->mutex); 98711227fd1SGeert Uytterhoeven 98813a5e30cSGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 98911227fd1SGeert Uytterhoeven 99011227fd1SGeert Uytterhoeven return 0; 99113a5e30cSGeoff Levand 99213a5e30cSGeoff Levand fail: 99313a5e30cSGeoff Levand kfree(ps3av); 99413a5e30cSGeoff Levand ps3av = NULL; 995e7eec2fcSroel kluin return res; 99611227fd1SGeert Uytterhoeven } 99711227fd1SGeert Uytterhoeven 99813a5e30cSGeoff Levand static int ps3av_remove(struct ps3_system_bus_device *dev) 99911227fd1SGeert Uytterhoeven { 100013a5e30cSGeoff Levand dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 100113a5e30cSGeoff Levand if (ps3av) { 100211227fd1SGeert Uytterhoeven ps3av_cmd_fin(); 1003f0200c02SBhaktipriya Shridhar flush_work(&ps3av->work); 100413a5e30cSGeoff Levand kfree(ps3av); 100513a5e30cSGeoff Levand ps3av = NULL; 100611227fd1SGeert Uytterhoeven } 100711227fd1SGeert Uytterhoeven 100813a5e30cSGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 100911227fd1SGeert Uytterhoeven return 0; 101011227fd1SGeert Uytterhoeven } 101111227fd1SGeert Uytterhoeven 101213a5e30cSGeoff Levand static void ps3av_shutdown(struct ps3_system_bus_device *dev) 101311227fd1SGeert Uytterhoeven { 101413a5e30cSGeoff Levand dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__); 101511227fd1SGeert Uytterhoeven ps3av_remove(dev); 101613a5e30cSGeoff Levand dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__); 101711227fd1SGeert Uytterhoeven } 101811227fd1SGeert Uytterhoeven 101911227fd1SGeert Uytterhoeven static struct ps3_vuart_port_driver ps3av_driver = { 102013a5e30cSGeoff Levand .core.match_id = PS3_MATCH_ID_AV_SETTINGS, 102113a5e30cSGeoff Levand .core.core.name = "ps3_av", 102211227fd1SGeert Uytterhoeven .probe = ps3av_probe, 102311227fd1SGeert Uytterhoeven .remove = ps3av_remove, 102411227fd1SGeert Uytterhoeven .shutdown = ps3av_shutdown, 102511227fd1SGeert Uytterhoeven }; 102611227fd1SGeert Uytterhoeven 1027a469f563SGeert Uytterhoeven static int __init ps3av_module_init(void) 102811227fd1SGeert Uytterhoeven { 1029ef596c69SGeert Uytterhoeven int error; 1030ef596c69SGeert Uytterhoeven 1031ef596c69SGeert Uytterhoeven if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 1032ef596c69SGeert Uytterhoeven return -ENODEV; 1033ef596c69SGeert Uytterhoeven 103413a5e30cSGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 103513a5e30cSGeoff Levand 1036ef596c69SGeert Uytterhoeven error = ps3_vuart_port_driver_register(&ps3av_driver); 103711227fd1SGeert Uytterhoeven if (error) { 103811227fd1SGeert Uytterhoeven printk(KERN_ERR 103911227fd1SGeert Uytterhoeven "%s: ps3_vuart_port_driver_register failed %d\n", 1040253f04e7SGeert Uytterhoeven __func__, error); 104111227fd1SGeert Uytterhoeven return error; 104211227fd1SGeert Uytterhoeven } 104311227fd1SGeert Uytterhoeven 104413a5e30cSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 104511227fd1SGeert Uytterhoeven return error; 104611227fd1SGeert Uytterhoeven } 104711227fd1SGeert Uytterhoeven 104811227fd1SGeert Uytterhoeven static void __exit ps3av_module_exit(void) 104911227fd1SGeert Uytterhoeven { 105013a5e30cSGeoff Levand pr_debug(" -> %s:%d\n", __func__, __LINE__); 105111227fd1SGeert Uytterhoeven ps3_vuart_port_driver_unregister(&ps3av_driver); 105213a5e30cSGeoff Levand pr_debug(" <- %s:%d\n", __func__, __LINE__); 105311227fd1SGeert Uytterhoeven } 105411227fd1SGeert Uytterhoeven 105511227fd1SGeert Uytterhoeven subsys_initcall(ps3av_module_init); 105611227fd1SGeert Uytterhoeven module_exit(ps3av_module_exit); 105713a5e30cSGeoff Levand 105813a5e30cSGeoff Levand MODULE_LICENSE("GPL v2"); 105913a5e30cSGeoff Levand MODULE_DESCRIPTION("PS3 AV Settings Driver"); 106013a5e30cSGeoff Levand MODULE_AUTHOR("Sony Computer Entertainment Inc."); 106113a5e30cSGeoff Levand MODULE_ALIAS(PS3_MODULE_ALIAS_AV_SETTINGS); 1062