brintos

brintos / linux-shallow public Read only

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