162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  PS3 AV backend support.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2007 Sony Computer Entertainment Inc.
662306a36Sopenharmony_ci *  Copyright 2007 Sony Corp.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/notifier.h>
1362306a36Sopenharmony_ci#include <linux/ioctl.h>
1462306a36Sopenharmony_ci#include <linux/slab.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <asm/firmware.h>
1762306a36Sopenharmony_ci#include <asm/ps3av.h>
1862306a36Sopenharmony_ci#include <asm/ps3.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <video/cmdline.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "vuart.h"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define BUFSIZE          4096	/* vuart buf size */
2562306a36Sopenharmony_ci#define PS3AV_BUF_SIZE   512	/* max packet size */
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int safe_mode;
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic int timeout = 5000;	/* in msec ( 5 sec ) */
3062306a36Sopenharmony_cimodule_param(timeout, int, 0644);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct ps3av {
3362306a36Sopenharmony_ci	struct mutex mutex;
3462306a36Sopenharmony_ci	struct work_struct work;
3562306a36Sopenharmony_ci	struct completion done;
3662306a36Sopenharmony_ci	int open_count;
3762306a36Sopenharmony_ci	struct ps3_system_bus_device *dev;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	int region;
4062306a36Sopenharmony_ci	struct ps3av_pkt_av_get_hw_conf av_hw_conf;
4162306a36Sopenharmony_ci	u32 av_port[PS3AV_AV_PORT_MAX + PS3AV_OPT_PORT_MAX];
4262306a36Sopenharmony_ci	u32 opt_port[PS3AV_OPT_PORT_MAX];
4362306a36Sopenharmony_ci	u32 head[PS3AV_HEAD_MAX];
4462306a36Sopenharmony_ci	u32 audio_port;
4562306a36Sopenharmony_ci	int ps3av_mode;
4662306a36Sopenharmony_ci	int ps3av_mode_old;
4762306a36Sopenharmony_ci	union {
4862306a36Sopenharmony_ci		struct ps3av_reply_hdr reply_hdr;
4962306a36Sopenharmony_ci		u8 raw[PS3AV_BUF_SIZE];
5062306a36Sopenharmony_ci	} recv_buf;
5162306a36Sopenharmony_ci} *ps3av;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* color space */
5462306a36Sopenharmony_ci#define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8
5562306a36Sopenharmony_ci#define RGB8   PS3AV_CMD_VIDEO_CS_RGB_8
5662306a36Sopenharmony_ci/* format */
5762306a36Sopenharmony_ci#define XRGB   PS3AV_CMD_VIDEO_FMT_X8R8G8B8
5862306a36Sopenharmony_ci/* aspect */
5962306a36Sopenharmony_ci#define A_N    PS3AV_CMD_AV_ASPECT_4_3
6062306a36Sopenharmony_ci#define A_W    PS3AV_CMD_AV_ASPECT_16_9
6162306a36Sopenharmony_cistatic const struct avset_video_mode {
6262306a36Sopenharmony_ci	u32 cs;
6362306a36Sopenharmony_ci	u32 fmt;
6462306a36Sopenharmony_ci	u32 vid;
6562306a36Sopenharmony_ci	u32 aspect;
6662306a36Sopenharmony_ci	u32 x;
6762306a36Sopenharmony_ci	u32 y;
6862306a36Sopenharmony_ci} video_mode_table[] = {
6962306a36Sopenharmony_ci	{     0, }, /* auto */
7062306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I,       A_N,  720,  480},
7162306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P,       A_N,  720,  480},
7262306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ,  A_W, 1280,  720},
7362306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080},
7462306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080},
7562306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I,       A_N,  720,  576},
7662306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P,       A_N,  720,  576},
7762306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ,  A_W, 1280,  720},
7862306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080},
7962306a36Sopenharmony_ci	{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080},
8062306a36Sopenharmony_ci	{  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA,       A_W, 1280,  768},
8162306a36Sopenharmony_ci	{  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA,       A_N, 1280, 1024},
8262306a36Sopenharmony_ci	{  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA,      A_W, 1920, 1200},
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* supported CIDs */
8662306a36Sopenharmony_cistatic u32 cmd_table[] = {
8762306a36Sopenharmony_ci	/* init */
8862306a36Sopenharmony_ci	PS3AV_CID_AV_INIT,
8962306a36Sopenharmony_ci	PS3AV_CID_AV_FIN,
9062306a36Sopenharmony_ci	PS3AV_CID_VIDEO_INIT,
9162306a36Sopenharmony_ci	PS3AV_CID_AUDIO_INIT,
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* set */
9462306a36Sopenharmony_ci	PS3AV_CID_AV_ENABLE_EVENT,
9562306a36Sopenharmony_ci	PS3AV_CID_AV_DISABLE_EVENT,
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	PS3AV_CID_AV_VIDEO_CS,
9862306a36Sopenharmony_ci	PS3AV_CID_AV_VIDEO_MUTE,
9962306a36Sopenharmony_ci	PS3AV_CID_AV_VIDEO_DISABLE_SIG,
10062306a36Sopenharmony_ci	PS3AV_CID_AV_AUDIO_PARAM,
10162306a36Sopenharmony_ci	PS3AV_CID_AV_AUDIO_MUTE,
10262306a36Sopenharmony_ci	PS3AV_CID_AV_HDMI_MODE,
10362306a36Sopenharmony_ci	PS3AV_CID_AV_TV_MUTE,
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	PS3AV_CID_VIDEO_MODE,
10662306a36Sopenharmony_ci	PS3AV_CID_VIDEO_FORMAT,
10762306a36Sopenharmony_ci	PS3AV_CID_VIDEO_PITCH,
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	PS3AV_CID_AUDIO_MODE,
11062306a36Sopenharmony_ci	PS3AV_CID_AUDIO_MUTE,
11162306a36Sopenharmony_ci	PS3AV_CID_AUDIO_ACTIVE,
11262306a36Sopenharmony_ci	PS3AV_CID_AUDIO_INACTIVE,
11362306a36Sopenharmony_ci	PS3AV_CID_AVB_PARAM,
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* get */
11662306a36Sopenharmony_ci	PS3AV_CID_AV_GET_HW_CONF,
11762306a36Sopenharmony_ci	PS3AV_CID_AV_GET_MONITOR_INFO,
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	/* event */
12062306a36Sopenharmony_ci	PS3AV_CID_EVENT_UNPLUGGED,
12162306a36Sopenharmony_ci	PS3AV_CID_EVENT_PLUGGED,
12262306a36Sopenharmony_ci	PS3AV_CID_EVENT_HDCP_DONE,
12362306a36Sopenharmony_ci	PS3AV_CID_EVENT_HDCP_FAIL,
12462306a36Sopenharmony_ci	PS3AV_CID_EVENT_HDCP_AUTH,
12562306a36Sopenharmony_ci	PS3AV_CID_EVENT_HDCP_ERROR,
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	0
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci#define PS3AV_EVENT_CMD_MASK           0x10000000
13162306a36Sopenharmony_ci#define PS3AV_EVENT_ID_MASK            0x0000ffff
13262306a36Sopenharmony_ci#define PS3AV_CID_MASK                 0xffffffff
13362306a36Sopenharmony_ci#define PS3AV_REPLY_BIT                0x80000000
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci#define ps3av_event_get_port_id(cid)   ((cid >> 16) & 0xff)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic u32 *ps3av_search_cmd_table(u32 cid, u32 mask)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	u32 *table;
14062306a36Sopenharmony_ci	int i;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	table = cmd_table;
14362306a36Sopenharmony_ci	for (i = 0;; table++, i++) {
14462306a36Sopenharmony_ci		if ((*table & mask) == (cid & mask))
14562306a36Sopenharmony_ci			break;
14662306a36Sopenharmony_ci		if (*table == 0)
14762306a36Sopenharmony_ci			return NULL;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci	return table;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	u32 *table;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (hdr->cid & PS3AV_EVENT_CMD_MASK) {
15762306a36Sopenharmony_ci		table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK);
15862306a36Sopenharmony_ci		if (table)
15962306a36Sopenharmony_ci			dev_dbg(&ps3av->dev->core,
16062306a36Sopenharmony_ci				"recv event packet cid:%08x port:0x%x size:%d\n",
16162306a36Sopenharmony_ci				hdr->cid, ps3av_event_get_port_id(hdr->cid),
16262306a36Sopenharmony_ci				hdr->size);
16362306a36Sopenharmony_ci		else
16462306a36Sopenharmony_ci			printk(KERN_ERR
16562306a36Sopenharmony_ci			       "%s: failed event packet, cid:%08x size:%d\n",
16662306a36Sopenharmony_ci			       __func__, hdr->cid, hdr->size);
16762306a36Sopenharmony_ci		return 1;	/* receive event packet */
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	return 0;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci#define POLLING_INTERVAL  25	/* in msec */
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic int ps3av_vuart_write(struct ps3_system_bus_device *dev,
17662306a36Sopenharmony_ci			     const void *buf, unsigned long size)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	int error;
17962306a36Sopenharmony_ci	dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
18062306a36Sopenharmony_ci	error = ps3_vuart_write(dev, buf, size);
18162306a36Sopenharmony_ci	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
18262306a36Sopenharmony_ci	return error ? error : size;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int ps3av_vuart_read(struct ps3_system_bus_device *dev, void *buf,
18662306a36Sopenharmony_ci			    unsigned long size, int timeout)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int error;
18962306a36Sopenharmony_ci	int loopcnt = 0;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
19262306a36Sopenharmony_ci	timeout = (timeout + POLLING_INTERVAL - 1) / POLLING_INTERVAL;
19362306a36Sopenharmony_ci	while (loopcnt++ <= timeout) {
19462306a36Sopenharmony_ci		error = ps3_vuart_read(dev, buf, size);
19562306a36Sopenharmony_ci		if (!error)
19662306a36Sopenharmony_ci			return size;
19762306a36Sopenharmony_ci		if (error != -EAGAIN) {
19862306a36Sopenharmony_ci			printk(KERN_ERR "%s: ps3_vuart_read failed %d\n",
19962306a36Sopenharmony_ci			       __func__, error);
20062306a36Sopenharmony_ci			return error;
20162306a36Sopenharmony_ci		}
20262306a36Sopenharmony_ci		msleep(POLLING_INTERVAL);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	return -EWOULDBLOCK;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic int ps3av_send_cmd_pkt(const struct ps3av_send_hdr *send_buf,
20862306a36Sopenharmony_ci			      struct ps3av_reply_hdr *recv_buf, int write_len,
20962306a36Sopenharmony_ci			      int read_len)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	int res;
21262306a36Sopenharmony_ci	u32 cmd;
21362306a36Sopenharmony_ci	int event;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!ps3av)
21662306a36Sopenharmony_ci		return -ENODEV;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/* send pkt */
21962306a36Sopenharmony_ci	res = ps3av_vuart_write(ps3av->dev, send_buf, write_len);
22062306a36Sopenharmony_ci	if (res < 0) {
22162306a36Sopenharmony_ci		dev_warn(&ps3av->dev->core,
22262306a36Sopenharmony_ci			"%s:%d: ps3av_vuart_write() failed: %s\n", __func__,
22362306a36Sopenharmony_ci			__LINE__, ps3_result(res));
22462306a36Sopenharmony_ci		return res;
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* recv pkt */
22862306a36Sopenharmony_ci	cmd = send_buf->cid;
22962306a36Sopenharmony_ci	do {
23062306a36Sopenharmony_ci		/* read header */
23162306a36Sopenharmony_ci		res = ps3av_vuart_read(ps3av->dev, recv_buf, PS3AV_HDR_SIZE,
23262306a36Sopenharmony_ci				       timeout);
23362306a36Sopenharmony_ci		if (res != PS3AV_HDR_SIZE) {
23462306a36Sopenharmony_ci			dev_warn(&ps3av->dev->core,
23562306a36Sopenharmony_ci				"%s:%d: ps3av_vuart_read() failed: %s\n", __func__,
23662306a36Sopenharmony_ci				__LINE__, ps3_result(res));
23762306a36Sopenharmony_ci			return res;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		/* read body */
24162306a36Sopenharmony_ci		res = ps3av_vuart_read(ps3av->dev, &recv_buf->cid,
24262306a36Sopenharmony_ci				       recv_buf->size, timeout);
24362306a36Sopenharmony_ci		if (res < 0) {
24462306a36Sopenharmony_ci			dev_warn(&ps3av->dev->core,
24562306a36Sopenharmony_ci				"%s:%d: ps3av_vuart_read() failed: %s\n", __func__,
24662306a36Sopenharmony_ci				__LINE__, ps3_result(res));
24762306a36Sopenharmony_ci			return res;
24862306a36Sopenharmony_ci		}
24962306a36Sopenharmony_ci		res += PS3AV_HDR_SIZE;	/* total len */
25062306a36Sopenharmony_ci		event = ps3av_parse_event_packet(recv_buf);
25162306a36Sopenharmony_ci		/* ret > 0 event packet */
25262306a36Sopenharmony_ci	} while (event);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if ((cmd | PS3AV_REPLY_BIT) != recv_buf->cid) {
25562306a36Sopenharmony_ci		dev_warn(&ps3av->dev->core, "%s:%d: reply err: %x\n", __func__,
25662306a36Sopenharmony_ci			__LINE__, recv_buf->cid);
25762306a36Sopenharmony_ci		return -EINVAL;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	return 0;
26162306a36Sopenharmony_ci}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int ps3av_process_reply_packet(struct ps3av_send_hdr *cmd_buf,
26462306a36Sopenharmony_ci				      const struct ps3av_reply_hdr *recv_buf,
26562306a36Sopenharmony_ci				      int user_buf_size)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int return_len;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if (recv_buf->version != PS3AV_VERSION) {
27062306a36Sopenharmony_ci		dev_dbg(&ps3av->dev->core, "reply_packet invalid version:%x\n",
27162306a36Sopenharmony_ci			recv_buf->version);
27262306a36Sopenharmony_ci		return -EFAULT;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	return_len = recv_buf->size + PS3AV_HDR_SIZE;
27562306a36Sopenharmony_ci	if (return_len > user_buf_size)
27662306a36Sopenharmony_ci		return_len = user_buf_size;
27762306a36Sopenharmony_ci	memcpy(cmd_buf, recv_buf, return_len);
27862306a36Sopenharmony_ci	return 0;		/* success */
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_civoid ps3av_set_hdr(u32 cid, u16 size, struct ps3av_send_hdr *hdr)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	hdr->version = PS3AV_VERSION;
28462306a36Sopenharmony_ci	hdr->size = size - PS3AV_HDR_SIZE;
28562306a36Sopenharmony_ci	hdr->cid = cid;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ciint ps3av_do_pkt(u32 cid, u16 send_len, size_t usr_buf_size,
28962306a36Sopenharmony_ci		 struct ps3av_send_hdr *buf)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	int res = 0;
29262306a36Sopenharmony_ci	u32 *table;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	BUG_ON(!ps3av);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	mutex_lock(&ps3av->mutex);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	table = ps3av_search_cmd_table(cid, PS3AV_CID_MASK);
29962306a36Sopenharmony_ci	BUG_ON(!table);
30062306a36Sopenharmony_ci	BUG_ON(send_len < PS3AV_HDR_SIZE);
30162306a36Sopenharmony_ci	BUG_ON(usr_buf_size < send_len);
30262306a36Sopenharmony_ci	BUG_ON(usr_buf_size > PS3AV_BUF_SIZE);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	/* create header */
30562306a36Sopenharmony_ci	ps3av_set_hdr(cid, send_len, buf);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	/* send packet via vuart */
30862306a36Sopenharmony_ci	res = ps3av_send_cmd_pkt(buf, &ps3av->recv_buf.reply_hdr, send_len,
30962306a36Sopenharmony_ci				 usr_buf_size);
31062306a36Sopenharmony_ci	if (res < 0) {
31162306a36Sopenharmony_ci		printk(KERN_ERR
31262306a36Sopenharmony_ci		       "%s: ps3av_send_cmd_pkt() failed (result=%d)\n",
31362306a36Sopenharmony_ci		       __func__, res);
31462306a36Sopenharmony_ci		goto err;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* process reply packet */
31862306a36Sopenharmony_ci	res = ps3av_process_reply_packet(buf, &ps3av->recv_buf.reply_hdr,
31962306a36Sopenharmony_ci					 usr_buf_size);
32062306a36Sopenharmony_ci	if (res < 0) {
32162306a36Sopenharmony_ci		printk(KERN_ERR "%s: put_return_status() failed (result=%d)\n",
32262306a36Sopenharmony_ci		       __func__, res);
32362306a36Sopenharmony_ci		goto err;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	mutex_unlock(&ps3av->mutex);
32762306a36Sopenharmony_ci	return 0;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cierr:
33062306a36Sopenharmony_ci	mutex_unlock(&ps3av->mutex);
33162306a36Sopenharmony_ci	printk(KERN_ERR "%s: failed cid:%x res:%d\n", __func__, cid, res);
33262306a36Sopenharmony_ci	return res;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int ps3av_set_av_video_mute(u32 mute)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	int i, num_of_av_port, res;
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	num_of_av_port = ps3av->av_hw_conf.num_of_hdmi +
34062306a36Sopenharmony_ci			 ps3av->av_hw_conf.num_of_avmulti;
34162306a36Sopenharmony_ci	/* video mute on */
34262306a36Sopenharmony_ci	for (i = 0; i < num_of_av_port; i++) {
34362306a36Sopenharmony_ci		res = ps3av_cmd_av_video_mute(1, &ps3av->av_port[i], mute);
34462306a36Sopenharmony_ci		if (res < 0)
34562306a36Sopenharmony_ci			return -1;
34662306a36Sopenharmony_ci	}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cistatic int ps3av_set_video_disable_sig(void)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	int i, num_of_hdmi_port, num_of_av_port, res;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	num_of_hdmi_port = ps3av->av_hw_conf.num_of_hdmi;
35662306a36Sopenharmony_ci	num_of_av_port = ps3av->av_hw_conf.num_of_hdmi +
35762306a36Sopenharmony_ci			 ps3av->av_hw_conf.num_of_avmulti;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	/* tv mute */
36062306a36Sopenharmony_ci	for (i = 0; i < num_of_hdmi_port; i++) {
36162306a36Sopenharmony_ci		res = ps3av_cmd_av_tv_mute(ps3av->av_port[i],
36262306a36Sopenharmony_ci					   PS3AV_CMD_MUTE_ON);
36362306a36Sopenharmony_ci		if (res < 0)
36462306a36Sopenharmony_ci			return -1;
36562306a36Sopenharmony_ci	}
36662306a36Sopenharmony_ci	msleep(100);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* video mute on */
36962306a36Sopenharmony_ci	for (i = 0; i < num_of_av_port; i++) {
37062306a36Sopenharmony_ci		res = ps3av_cmd_av_video_disable_sig(ps3av->av_port[i]);
37162306a36Sopenharmony_ci		if (res < 0)
37262306a36Sopenharmony_ci			return -1;
37362306a36Sopenharmony_ci		if (i < num_of_hdmi_port) {
37462306a36Sopenharmony_ci			res = ps3av_cmd_av_tv_mute(ps3av->av_port[i],
37562306a36Sopenharmony_ci						   PS3AV_CMD_MUTE_OFF);
37662306a36Sopenharmony_ci			if (res < 0)
37762306a36Sopenharmony_ci				return -1;
37862306a36Sopenharmony_ci		}
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci	msleep(300);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return 0;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_cistatic int ps3av_set_audio_mute(u32 mute)
38662306a36Sopenharmony_ci{
38762306a36Sopenharmony_ci	int i, num_of_av_port, num_of_opt_port, res;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	num_of_av_port = ps3av->av_hw_conf.num_of_hdmi +
39062306a36Sopenharmony_ci			 ps3av->av_hw_conf.num_of_avmulti;
39162306a36Sopenharmony_ci	num_of_opt_port = ps3av->av_hw_conf.num_of_spdif;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	for (i = 0; i < num_of_av_port; i++) {
39462306a36Sopenharmony_ci		res = ps3av_cmd_av_audio_mute(1, &ps3av->av_port[i], mute);
39562306a36Sopenharmony_ci		if (res < 0)
39662306a36Sopenharmony_ci			return -1;
39762306a36Sopenharmony_ci	}
39862306a36Sopenharmony_ci	for (i = 0; i < num_of_opt_port; i++) {
39962306a36Sopenharmony_ci		res = ps3av_cmd_audio_mute(1, &ps3av->opt_port[i], mute);
40062306a36Sopenharmony_ci		if (res < 0)
40162306a36Sopenharmony_ci			return -1;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	return 0;
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ciint ps3av_set_audio_mode(u32 ch, u32 fs, u32 word_bits, u32 format, u32 source)
40862306a36Sopenharmony_ci{
40962306a36Sopenharmony_ci	struct ps3av_pkt_avb_param avb_param;
41062306a36Sopenharmony_ci	int i, num_of_audio, vid, res;
41162306a36Sopenharmony_ci	struct ps3av_pkt_audio_mode audio_mode;
41262306a36Sopenharmony_ci	u32 len = 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	num_of_audio = ps3av->av_hw_conf.num_of_hdmi +
41562306a36Sopenharmony_ci		       ps3av->av_hw_conf.num_of_avmulti +
41662306a36Sopenharmony_ci		       ps3av->av_hw_conf.num_of_spdif;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	avb_param.num_of_video_pkt = 0;
41962306a36Sopenharmony_ci	avb_param.num_of_audio_pkt = PS3AV_AVB_NUM_AUDIO;	/* always 0 */
42062306a36Sopenharmony_ci	avb_param.num_of_av_video_pkt = 0;
42162306a36Sopenharmony_ci	avb_param.num_of_av_audio_pkt = ps3av->av_hw_conf.num_of_hdmi;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	vid = video_mode_table[ps3av->ps3av_mode].vid;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	/* audio mute */
42662306a36Sopenharmony_ci	ps3av_set_audio_mute(PS3AV_CMD_MUTE_ON);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	/* audio inactive */
42962306a36Sopenharmony_ci	res = ps3av_cmd_audio_active(0, ps3av->audio_port);
43062306a36Sopenharmony_ci	if (res < 0)
43162306a36Sopenharmony_ci		dev_dbg(&ps3av->dev->core,
43262306a36Sopenharmony_ci			"ps3av_cmd_audio_active OFF failed\n");
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	/* audio_pkt */
43562306a36Sopenharmony_ci	for (i = 0; i < num_of_audio; i++) {
43662306a36Sopenharmony_ci		ps3av_cmd_set_audio_mode(&audio_mode, ps3av->av_port[i], ch,
43762306a36Sopenharmony_ci					 fs, word_bits, format, source);
43862306a36Sopenharmony_ci		if (i < ps3av->av_hw_conf.num_of_hdmi) {
43962306a36Sopenharmony_ci			/* hdmi only */
44062306a36Sopenharmony_ci			len += ps3av_cmd_set_av_audio_param(&avb_param.buf[len],
44162306a36Sopenharmony_ci							    ps3av->av_port[i],
44262306a36Sopenharmony_ci							    &audio_mode, vid);
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci		/* audio_mode pkt should be sent separately */
44562306a36Sopenharmony_ci		res = ps3av_cmd_audio_mode(&audio_mode);
44662306a36Sopenharmony_ci		if (res < 0)
44762306a36Sopenharmony_ci			dev_dbg(&ps3av->dev->core,
44862306a36Sopenharmony_ci				"ps3av_cmd_audio_mode failed, port:%x\n", i);
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	/* send command using avb pkt */
45262306a36Sopenharmony_ci	len += offsetof(struct ps3av_pkt_avb_param, buf);
45362306a36Sopenharmony_ci	res = ps3av_cmd_avb_param(&avb_param, len);
45462306a36Sopenharmony_ci	if (res < 0)
45562306a36Sopenharmony_ci		dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n");
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* audio mute */
45862306a36Sopenharmony_ci	ps3av_set_audio_mute(PS3AV_CMD_MUTE_OFF);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	/* audio active */
46162306a36Sopenharmony_ci	res = ps3av_cmd_audio_active(1, ps3av->audio_port);
46262306a36Sopenharmony_ci	if (res < 0)
46362306a36Sopenharmony_ci		dev_dbg(&ps3av->dev->core,
46462306a36Sopenharmony_ci			"ps3av_cmd_audio_active ON failed\n");
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return 0;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_set_audio_mode);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int ps3av_set_videomode(void)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	/* av video mute */
47362306a36Sopenharmony_ci	ps3av_set_av_video_mute(PS3AV_CMD_MUTE_ON);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	/* wake up ps3avd to do the actual video mode setting */
47662306a36Sopenharmony_ci	schedule_work(&ps3av->work);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return 0;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_cistatic void ps3av_set_videomode_packet(u32 id)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	struct ps3av_pkt_avb_param avb_param;
48462306a36Sopenharmony_ci	unsigned int i;
48562306a36Sopenharmony_ci	u32 len = 0, av_video_cs;
48662306a36Sopenharmony_ci	const struct avset_video_mode *video_mode;
48762306a36Sopenharmony_ci	int res;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	video_mode = &video_mode_table[id & PS3AV_MODE_MASK];
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	avb_param.num_of_video_pkt = PS3AV_AVB_NUM_VIDEO; /* num of head */
49262306a36Sopenharmony_ci	avb_param.num_of_audio_pkt = 0;
49362306a36Sopenharmony_ci	avb_param.num_of_av_video_pkt = ps3av->av_hw_conf.num_of_hdmi +
49462306a36Sopenharmony_ci					ps3av->av_hw_conf.num_of_avmulti;
49562306a36Sopenharmony_ci	avb_param.num_of_av_audio_pkt = 0;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	/* video_pkt */
49862306a36Sopenharmony_ci	for (i = 0; i < avb_param.num_of_video_pkt; i++)
49962306a36Sopenharmony_ci		len += ps3av_cmd_set_video_mode(&avb_param.buf[len],
50062306a36Sopenharmony_ci						ps3av->head[i], video_mode->vid,
50162306a36Sopenharmony_ci						video_mode->fmt, id);
50262306a36Sopenharmony_ci	/* av_video_pkt */
50362306a36Sopenharmony_ci	for (i = 0; i < avb_param.num_of_av_video_pkt; i++) {
50462306a36Sopenharmony_ci		if (id & PS3AV_MODE_DVI || id & PS3AV_MODE_RGB)
50562306a36Sopenharmony_ci			av_video_cs = RGB8;
50662306a36Sopenharmony_ci		else
50762306a36Sopenharmony_ci			av_video_cs = video_mode->cs;
50862306a36Sopenharmony_ci#ifndef PS3AV_HDMI_YUV
50962306a36Sopenharmony_ci		if (ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_0 ||
51062306a36Sopenharmony_ci		    ps3av->av_port[i] == PS3AV_CMD_AVPORT_HDMI_1)
51162306a36Sopenharmony_ci			av_video_cs = RGB8; /* use RGB for HDMI */
51262306a36Sopenharmony_ci#endif
51362306a36Sopenharmony_ci		len += ps3av_cmd_set_av_video_cs(&avb_param.buf[len],
51462306a36Sopenharmony_ci						 ps3av->av_port[i],
51562306a36Sopenharmony_ci						 video_mode->vid, av_video_cs,
51662306a36Sopenharmony_ci						 video_mode->aspect, id);
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci	/* send command using avb pkt */
51962306a36Sopenharmony_ci	len += offsetof(struct ps3av_pkt_avb_param, buf);
52062306a36Sopenharmony_ci	res = ps3av_cmd_avb_param(&avb_param, len);
52162306a36Sopenharmony_ci	if (res == PS3AV_STATUS_NO_SYNC_HEAD)
52262306a36Sopenharmony_ci		printk(KERN_WARNING
52362306a36Sopenharmony_ci		       "%s: Command failed. Please try your request again.\n",
52462306a36Sopenharmony_ci		       __func__);
52562306a36Sopenharmony_ci	else if (res)
52662306a36Sopenharmony_ci		dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n");
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void ps3av_set_videomode_cont(u32 id, u32 old_id)
53062306a36Sopenharmony_ci{
53162306a36Sopenharmony_ci	static int vesa;
53262306a36Sopenharmony_ci	int res;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	/* video signal off */
53562306a36Sopenharmony_ci	ps3av_set_video_disable_sig();
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/*
53862306a36Sopenharmony_ci	 * AV backend needs non-VESA mode setting at least one time
53962306a36Sopenharmony_ci	 * when VESA mode is used.
54062306a36Sopenharmony_ci	 */
54162306a36Sopenharmony_ci	if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) {
54262306a36Sopenharmony_ci		/* vesa mode */
54362306a36Sopenharmony_ci		ps3av_set_videomode_packet(PS3AV_MODE_480P);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	vesa = 1;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	/* Retail PS3 product doesn't support this */
54862306a36Sopenharmony_ci	if (id & PS3AV_MODE_HDCP_OFF) {
54962306a36Sopenharmony_ci		res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_HDCP_OFF);
55062306a36Sopenharmony_ci		if (res == PS3AV_STATUS_UNSUPPORTED_HDMI_MODE)
55162306a36Sopenharmony_ci			dev_dbg(&ps3av->dev->core, "Not supported\n");
55262306a36Sopenharmony_ci		else if (res)
55362306a36Sopenharmony_ci			dev_dbg(&ps3av->dev->core,
55462306a36Sopenharmony_ci				"ps3av_cmd_av_hdmi_mode failed\n");
55562306a36Sopenharmony_ci	} else if (old_id & PS3AV_MODE_HDCP_OFF) {
55662306a36Sopenharmony_ci		res = ps3av_cmd_av_hdmi_mode(PS3AV_CMD_AV_HDMI_MODE_NORMAL);
55762306a36Sopenharmony_ci		if (res < 0 && res != PS3AV_STATUS_UNSUPPORTED_HDMI_MODE)
55862306a36Sopenharmony_ci			dev_dbg(&ps3av->dev->core,
55962306a36Sopenharmony_ci				"ps3av_cmd_av_hdmi_mode failed\n");
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	ps3av_set_videomode_packet(id);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	msleep(1500);
56562306a36Sopenharmony_ci	/* av video mute */
56662306a36Sopenharmony_ci	ps3av_set_av_video_mute(PS3AV_CMD_MUTE_OFF);
56762306a36Sopenharmony_ci}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_cistatic void ps3avd(struct work_struct *work)
57062306a36Sopenharmony_ci{
57162306a36Sopenharmony_ci	ps3av_set_videomode_cont(ps3av->ps3av_mode, ps3av->ps3av_mode_old);
57262306a36Sopenharmony_ci	complete(&ps3av->done);
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci#define SHIFT_50	0
57662306a36Sopenharmony_ci#define SHIFT_60	4
57762306a36Sopenharmony_ci#define SHIFT_VESA	8
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_cistatic const struct {
58062306a36Sopenharmony_ci	unsigned mask:19;
58162306a36Sopenharmony_ci	unsigned id:4;
58262306a36Sopenharmony_ci} ps3av_preferred_modes[] = {
58362306a36Sopenharmony_ci	{ PS3AV_RESBIT_WUXGA      << SHIFT_VESA, PS3AV_MODE_WUXGA   },
58462306a36Sopenharmony_ci	{ PS3AV_RESBIT_1920x1080P << SHIFT_60,   PS3AV_MODE_1080P60 },
58562306a36Sopenharmony_ci	{ PS3AV_RESBIT_1920x1080P << SHIFT_50,   PS3AV_MODE_1080P50 },
58662306a36Sopenharmony_ci	{ PS3AV_RESBIT_1920x1080I << SHIFT_60,   PS3AV_MODE_1080I60 },
58762306a36Sopenharmony_ci	{ PS3AV_RESBIT_1920x1080I << SHIFT_50,   PS3AV_MODE_1080I50 },
58862306a36Sopenharmony_ci	{ PS3AV_RESBIT_SXGA       << SHIFT_VESA, PS3AV_MODE_SXGA    },
58962306a36Sopenharmony_ci	{ PS3AV_RESBIT_WXGA       << SHIFT_VESA, PS3AV_MODE_WXGA    },
59062306a36Sopenharmony_ci	{ PS3AV_RESBIT_1280x720P  << SHIFT_60,   PS3AV_MODE_720P60  },
59162306a36Sopenharmony_ci	{ PS3AV_RESBIT_1280x720P  << SHIFT_50,   PS3AV_MODE_720P50  },
59262306a36Sopenharmony_ci	{ PS3AV_RESBIT_720x480P   << SHIFT_60,   PS3AV_MODE_480P    },
59362306a36Sopenharmony_ci	{ PS3AV_RESBIT_720x576P   << SHIFT_50,   PS3AV_MODE_576P    },
59462306a36Sopenharmony_ci};
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic enum ps3av_mode_num ps3av_resbit2id(u32 res_50, u32 res_60,
59762306a36Sopenharmony_ci					   u32 res_vesa)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	unsigned int i;
60062306a36Sopenharmony_ci	u32 res_all;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/*
60362306a36Sopenharmony_ci	 * We mask off the resolution bits we care about and combine the
60462306a36Sopenharmony_ci	 * results in one bitfield, so make sure there's no overlap
60562306a36Sopenharmony_ci	 */
60662306a36Sopenharmony_ci	BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 &
60762306a36Sopenharmony_ci		     PS3AV_RES_MASK_60 << SHIFT_60);
60862306a36Sopenharmony_ci	BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 &
60962306a36Sopenharmony_ci		     PS3AV_RES_MASK_VESA << SHIFT_VESA);
61062306a36Sopenharmony_ci	BUILD_BUG_ON(PS3AV_RES_MASK_60 << SHIFT_60 &
61162306a36Sopenharmony_ci		     PS3AV_RES_MASK_VESA << SHIFT_VESA);
61262306a36Sopenharmony_ci	res_all = (res_50 & PS3AV_RES_MASK_50) << SHIFT_50 |
61362306a36Sopenharmony_ci		  (res_60 & PS3AV_RES_MASK_60) << SHIFT_60 |
61462306a36Sopenharmony_ci		  (res_vesa & PS3AV_RES_MASK_VESA) << SHIFT_VESA;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (!res_all)
61762306a36Sopenharmony_ci		return 0;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ps3av_preferred_modes); i++)
62062306a36Sopenharmony_ci		if (res_all & ps3av_preferred_modes[i].mask)
62162306a36Sopenharmony_ci			return ps3av_preferred_modes[i].id;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	return 0;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistatic enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	enum ps3av_mode_num id;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (safe_mode)
63162306a36Sopenharmony_ci		return PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* check native resolution */
63462306a36Sopenharmony_ci	id = ps3av_resbit2id(info->res_50.native, info->res_60.native,
63562306a36Sopenharmony_ci			     info->res_vesa.native);
63662306a36Sopenharmony_ci	if (id) {
63762306a36Sopenharmony_ci		pr_debug("%s: Using native mode %d\n", __func__, id);
63862306a36Sopenharmony_ci		return id;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* check supported resolutions */
64262306a36Sopenharmony_ci	id = ps3av_resbit2id(info->res_50.res_bits, info->res_60.res_bits,
64362306a36Sopenharmony_ci			     info->res_vesa.res_bits);
64462306a36Sopenharmony_ci	if (id) {
64562306a36Sopenharmony_ci		pr_debug("%s: Using supported mode %d\n", __func__, id);
64662306a36Sopenharmony_ci		return id;
64762306a36Sopenharmony_ci	}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (ps3av->region & PS3AV_REGION_60)
65062306a36Sopenharmony_ci		id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
65162306a36Sopenharmony_ci	else
65262306a36Sopenharmony_ci		id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;
65362306a36Sopenharmony_ci	pr_debug("%s: Using default mode %d\n", __func__, id);
65462306a36Sopenharmony_ci	return id;
65562306a36Sopenharmony_ci}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cistatic void ps3av_monitor_info_dump(
65862306a36Sopenharmony_ci	const struct ps3av_pkt_av_get_monitor_info *monitor_info)
65962306a36Sopenharmony_ci{
66062306a36Sopenharmony_ci	const struct ps3av_info_monitor *info = &monitor_info->info;
66162306a36Sopenharmony_ci	const struct ps3av_info_audio *audio = info->audio;
66262306a36Sopenharmony_ci	char id[sizeof(info->monitor_id)*3+1];
66362306a36Sopenharmony_ci	int i;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	pr_debug("Monitor Info: size %u\n", monitor_info->send_hdr.size);
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	pr_debug("avport: %02x\n", info->avport);
66862306a36Sopenharmony_ci	for (i = 0; i < sizeof(info->monitor_id); i++)
66962306a36Sopenharmony_ci		sprintf(&id[i*3], " %02x", info->monitor_id[i]);
67062306a36Sopenharmony_ci	pr_debug("monitor_id: %s\n", id);
67162306a36Sopenharmony_ci	pr_debug("monitor_type: %02x\n", info->monitor_type);
67262306a36Sopenharmony_ci	pr_debug("monitor_name: %.*s\n", (int)sizeof(info->monitor_name),
67362306a36Sopenharmony_ci		 info->monitor_name);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* resolution */
67662306a36Sopenharmony_ci	pr_debug("resolution_60: bits: %08x native: %08x\n",
67762306a36Sopenharmony_ci		 info->res_60.res_bits, info->res_60.native);
67862306a36Sopenharmony_ci	pr_debug("resolution_50: bits: %08x native: %08x\n",
67962306a36Sopenharmony_ci		 info->res_50.res_bits, info->res_50.native);
68062306a36Sopenharmony_ci	pr_debug("resolution_other: bits: %08x native: %08x\n",
68162306a36Sopenharmony_ci		 info->res_other.res_bits, info->res_other.native);
68262306a36Sopenharmony_ci	pr_debug("resolution_vesa: bits: %08x native: %08x\n",
68362306a36Sopenharmony_ci		 info->res_vesa.res_bits, info->res_vesa.native);
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* color space */
68662306a36Sopenharmony_ci	pr_debug("color space    rgb: %02x\n", info->cs.rgb);
68762306a36Sopenharmony_ci	pr_debug("color space yuv444: %02x\n", info->cs.yuv444);
68862306a36Sopenharmony_ci	pr_debug("color space yuv422: %02x\n", info->cs.yuv422);
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/* color info */
69162306a36Sopenharmony_ci	pr_debug("color info   red: X %04x Y %04x\n", info->color.red_x,
69262306a36Sopenharmony_ci		 info->color.red_y);
69362306a36Sopenharmony_ci	pr_debug("color info green: X %04x Y %04x\n", info->color.green_x,
69462306a36Sopenharmony_ci		 info->color.green_y);
69562306a36Sopenharmony_ci	pr_debug("color info  blue: X %04x Y %04x\n", info->color.blue_x,
69662306a36Sopenharmony_ci		 info->color.blue_y);
69762306a36Sopenharmony_ci	pr_debug("color info white: X %04x Y %04x\n", info->color.white_x,
69862306a36Sopenharmony_ci		 info->color.white_y);
69962306a36Sopenharmony_ci	pr_debug("color info gamma:  %08x\n", info->color.gamma);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	/* other info */
70262306a36Sopenharmony_ci	pr_debug("supported_AI: %02x\n", info->supported_ai);
70362306a36Sopenharmony_ci	pr_debug("speaker_info: %02x\n", info->speaker_info);
70462306a36Sopenharmony_ci	pr_debug("num of audio: %02x\n", info->num_of_audio_block);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	/* audio block */
70762306a36Sopenharmony_ci	for (i = 0; i < info->num_of_audio_block; i++) {
70862306a36Sopenharmony_ci		pr_debug(
70962306a36Sopenharmony_ci			"audio[%d] type: %02x max_ch: %02x fs: %02x sbit: %02x\n",
71062306a36Sopenharmony_ci			 i, audio->type, audio->max_num_of_ch, audio->fs,
71162306a36Sopenharmony_ci			 audio->sbit);
71262306a36Sopenharmony_ci		audio++;
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic const struct ps3av_monitor_quirk {
71762306a36Sopenharmony_ci	const char *monitor_name;
71862306a36Sopenharmony_ci	u32 clear_60;
71962306a36Sopenharmony_ci} ps3av_monitor_quirks[] = {
72062306a36Sopenharmony_ci	{
72162306a36Sopenharmony_ci		.monitor_name	= "DELL 2007WFP",
72262306a36Sopenharmony_ci		.clear_60	= PS3AV_RESBIT_1920x1080I
72362306a36Sopenharmony_ci	}, {
72462306a36Sopenharmony_ci		.monitor_name	= "L226WTQ",
72562306a36Sopenharmony_ci		.clear_60	= PS3AV_RESBIT_1920x1080I |
72662306a36Sopenharmony_ci				  PS3AV_RESBIT_1920x1080P
72762306a36Sopenharmony_ci	}, {
72862306a36Sopenharmony_ci		.monitor_name	= "SyncMaster",
72962306a36Sopenharmony_ci		.clear_60	= PS3AV_RESBIT_1920x1080I
73062306a36Sopenharmony_ci	}
73162306a36Sopenharmony_ci};
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic void ps3av_fixup_monitor_info(struct ps3av_info_monitor *info)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	unsigned int i;
73662306a36Sopenharmony_ci	const struct ps3av_monitor_quirk *quirk;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ps3av_monitor_quirks); i++) {
73962306a36Sopenharmony_ci		quirk = &ps3av_monitor_quirks[i];
74062306a36Sopenharmony_ci		if (!strncmp(info->monitor_name, quirk->monitor_name,
74162306a36Sopenharmony_ci			     sizeof(info->monitor_name))) {
74262306a36Sopenharmony_ci			pr_info("%s: Applying quirk for %s\n", __func__,
74362306a36Sopenharmony_ci				quirk->monitor_name);
74462306a36Sopenharmony_ci			info->res_60.res_bits &= ~quirk->clear_60;
74562306a36Sopenharmony_ci			info->res_60.native &= ~quirk->clear_60;
74662306a36Sopenharmony_ci			break;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_cistatic int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf)
75262306a36Sopenharmony_ci{
75362306a36Sopenharmony_ci	int i, res, id = 0, dvi = 0, rgb = 0;
75462306a36Sopenharmony_ci	struct ps3av_pkt_av_get_monitor_info monitor_info;
75562306a36Sopenharmony_ci	struct ps3av_info_monitor *info;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* get mode id for hdmi */
75862306a36Sopenharmony_ci	for (i = 0; i < av_hw_conf->num_of_hdmi && !id; i++) {
75962306a36Sopenharmony_ci		res = ps3av_cmd_video_get_monitor_info(&monitor_info,
76062306a36Sopenharmony_ci						       PS3AV_CMD_AVPORT_HDMI_0 +
76162306a36Sopenharmony_ci						       i);
76262306a36Sopenharmony_ci		if (res < 0)
76362306a36Sopenharmony_ci			return -1;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci		ps3av_monitor_info_dump(&monitor_info);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci		info = &monitor_info.info;
76862306a36Sopenharmony_ci		ps3av_fixup_monitor_info(info);
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		switch (info->monitor_type) {
77162306a36Sopenharmony_ci		case PS3AV_MONITOR_TYPE_DVI:
77262306a36Sopenharmony_ci			dvi = PS3AV_MODE_DVI;
77362306a36Sopenharmony_ci			fallthrough;
77462306a36Sopenharmony_ci		case PS3AV_MONITOR_TYPE_HDMI:
77562306a36Sopenharmony_ci			id = ps3av_hdmi_get_id(info);
77662306a36Sopenharmony_ci			break;
77762306a36Sopenharmony_ci		}
77862306a36Sopenharmony_ci	}
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (!id) {
78162306a36Sopenharmony_ci		/* no HDMI interface or HDMI is off */
78262306a36Sopenharmony_ci		if (ps3av->region & PS3AV_REGION_60)
78362306a36Sopenharmony_ci			id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_60;
78462306a36Sopenharmony_ci		else
78562306a36Sopenharmony_ci			id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_50;
78662306a36Sopenharmony_ci		if (ps3av->region & PS3AV_REGION_RGB)
78762306a36Sopenharmony_ci			rgb = PS3AV_MODE_RGB;
78862306a36Sopenharmony_ci		pr_debug("%s: Using avmulti mode %d\n", __func__, id);
78962306a36Sopenharmony_ci	}
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	return id | dvi | rgb;
79262306a36Sopenharmony_ci}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cistatic int ps3av_get_hw_conf(struct ps3av *ps3av)
79562306a36Sopenharmony_ci{
79662306a36Sopenharmony_ci	int i, j, k, res;
79762306a36Sopenharmony_ci	const struct ps3av_pkt_av_get_hw_conf *hw_conf;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* get av_hw_conf */
80062306a36Sopenharmony_ci	res = ps3av_cmd_av_get_hw_conf(&ps3av->av_hw_conf);
80162306a36Sopenharmony_ci	if (res < 0)
80262306a36Sopenharmony_ci		return -1;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	hw_conf = &ps3av->av_hw_conf;
80562306a36Sopenharmony_ci	pr_debug("av_h_conf: num of hdmi: %u\n", hw_conf->num_of_hdmi);
80662306a36Sopenharmony_ci	pr_debug("av_h_conf: num of avmulti: %u\n", hw_conf->num_of_avmulti);
80762306a36Sopenharmony_ci	pr_debug("av_h_conf: num of spdif: %u\n", hw_conf->num_of_spdif);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	for (i = 0; i < PS3AV_HEAD_MAX; i++)
81062306a36Sopenharmony_ci		ps3av->head[i] = PS3AV_CMD_VIDEO_HEAD_A + i;
81162306a36Sopenharmony_ci	for (i = 0; i < PS3AV_OPT_PORT_MAX; i++)
81262306a36Sopenharmony_ci		ps3av->opt_port[i] = PS3AV_CMD_AVPORT_SPDIF_0 + i;
81362306a36Sopenharmony_ci	for (i = 0; i < hw_conf->num_of_hdmi; i++)
81462306a36Sopenharmony_ci		ps3av->av_port[i] = PS3AV_CMD_AVPORT_HDMI_0 + i;
81562306a36Sopenharmony_ci	for (j = 0; j < hw_conf->num_of_avmulti; j++)
81662306a36Sopenharmony_ci		ps3av->av_port[i + j] = PS3AV_CMD_AVPORT_AVMULTI_0 + j;
81762306a36Sopenharmony_ci	for (k = 0; k < hw_conf->num_of_spdif; k++)
81862306a36Sopenharmony_ci		ps3av->av_port[i + j + k] = PS3AV_CMD_AVPORT_SPDIF_0 + k;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	/* set all audio port */
82162306a36Sopenharmony_ci	ps3av->audio_port = PS3AV_CMD_AUDIO_PORT_HDMI_0
82262306a36Sopenharmony_ci	    | PS3AV_CMD_AUDIO_PORT_HDMI_1
82362306a36Sopenharmony_ci	    | PS3AV_CMD_AUDIO_PORT_AVMULTI_0
82462306a36Sopenharmony_ci	    | PS3AV_CMD_AUDIO_PORT_SPDIF_0 | PS3AV_CMD_AUDIO_PORT_SPDIF_1;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	return 0;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/* set mode using id */
83062306a36Sopenharmony_ciint ps3av_set_video_mode(int id)
83162306a36Sopenharmony_ci{
83262306a36Sopenharmony_ci	int size;
83362306a36Sopenharmony_ci	u32 option;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	size = ARRAY_SIZE(video_mode_table);
83662306a36Sopenharmony_ci	if ((id & PS3AV_MODE_MASK) > size - 1 || id < 0) {
83762306a36Sopenharmony_ci		dev_dbg(&ps3av->dev->core, "%s: error id :%d\n", __func__, id);
83862306a36Sopenharmony_ci		return -EINVAL;
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* auto mode */
84262306a36Sopenharmony_ci	option = id & ~PS3AV_MODE_MASK;
84362306a36Sopenharmony_ci	if ((id & PS3AV_MODE_MASK) == PS3AV_MODE_AUTO) {
84462306a36Sopenharmony_ci		id = ps3av_auto_videomode(&ps3av->av_hw_conf);
84562306a36Sopenharmony_ci		if (id < 1) {
84662306a36Sopenharmony_ci			printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);
84762306a36Sopenharmony_ci			return -EINVAL;
84862306a36Sopenharmony_ci		}
84962306a36Sopenharmony_ci		id |= option;
85062306a36Sopenharmony_ci	}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci	/* set videomode */
85362306a36Sopenharmony_ci	wait_for_completion(&ps3av->done);
85462306a36Sopenharmony_ci	ps3av->ps3av_mode_old = ps3av->ps3av_mode;
85562306a36Sopenharmony_ci	ps3av->ps3av_mode = id;
85662306a36Sopenharmony_ci	if (ps3av_set_videomode())
85762306a36Sopenharmony_ci		ps3av->ps3av_mode = ps3av->ps3av_mode_old;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	return 0;
86062306a36Sopenharmony_ci}
86162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_set_video_mode);
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ciint ps3av_get_auto_mode(void)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	return ps3av_auto_videomode(&ps3av->av_hw_conf);
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_get_auto_mode);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ciint ps3av_get_mode(void)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	return ps3av ? ps3av->ps3av_mode : 0;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_get_mode);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci/* get resolution by video_mode */
87662306a36Sopenharmony_ciint ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	int size;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	id = id & PS3AV_MODE_MASK;
88162306a36Sopenharmony_ci	size = ARRAY_SIZE(video_mode_table);
88262306a36Sopenharmony_ci	if (id > size - 1 || id < 0) {
88362306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);
88462306a36Sopenharmony_ci		return -EINVAL;
88562306a36Sopenharmony_ci	}
88662306a36Sopenharmony_ci	*xres = video_mode_table[id].x;
88762306a36Sopenharmony_ci	*yres = video_mode_table[id].y;
88862306a36Sopenharmony_ci	return 0;
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_video_mode2res);
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci/* mute */
89362306a36Sopenharmony_ciint ps3av_video_mute(int mute)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	return ps3av_set_av_video_mute(mute ? PS3AV_CMD_MUTE_ON
89662306a36Sopenharmony_ci					    : PS3AV_CMD_MUTE_OFF);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_video_mute);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci/* mute analog output only */
90162306a36Sopenharmony_ciint ps3av_audio_mute_analog(int mute)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	int i, res;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	for (i = 0; i < ps3av->av_hw_conf.num_of_avmulti; i++) {
90662306a36Sopenharmony_ci		res = ps3av_cmd_av_audio_mute(1,
90762306a36Sopenharmony_ci			&ps3av->av_port[i + ps3av->av_hw_conf.num_of_hdmi],
90862306a36Sopenharmony_ci			mute);
90962306a36Sopenharmony_ci		if (res < 0)
91062306a36Sopenharmony_ci			return -1;
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci	return 0;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_audio_mute_analog);
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ciint ps3av_audio_mute(int mute)
91762306a36Sopenharmony_ci{
91862306a36Sopenharmony_ci	return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
91962306a36Sopenharmony_ci					 : PS3AV_CMD_MUTE_OFF);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ps3av_audio_mute);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_cistatic int ps3av_probe(struct ps3_system_bus_device *dev)
92462306a36Sopenharmony_ci{
92562306a36Sopenharmony_ci	const char *mode_option;
92662306a36Sopenharmony_ci	int res;
92762306a36Sopenharmony_ci	int id;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
93062306a36Sopenharmony_ci	dev_dbg(&dev->core, "  timeout=%d\n", timeout);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (ps3av) {
93362306a36Sopenharmony_ci		dev_err(&dev->core, "Only one ps3av device is supported\n");
93462306a36Sopenharmony_ci		return -EBUSY;
93562306a36Sopenharmony_ci	}
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	ps3av = kzalloc(sizeof(*ps3av), GFP_KERNEL);
93862306a36Sopenharmony_ci	if (!ps3av)
93962306a36Sopenharmony_ci		return -ENOMEM;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	mutex_init(&ps3av->mutex);
94262306a36Sopenharmony_ci	ps3av->ps3av_mode = PS3AV_MODE_AUTO;
94362306a36Sopenharmony_ci	ps3av->dev = dev;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	INIT_WORK(&ps3av->work, ps3avd);
94662306a36Sopenharmony_ci	init_completion(&ps3av->done);
94762306a36Sopenharmony_ci	complete(&ps3av->done);
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	switch (ps3_os_area_get_av_multi_out()) {
95062306a36Sopenharmony_ci	case PS3_PARAM_AV_MULTI_OUT_NTSC:
95162306a36Sopenharmony_ci		ps3av->region = PS3AV_REGION_60;
95262306a36Sopenharmony_ci		break;
95362306a36Sopenharmony_ci	case PS3_PARAM_AV_MULTI_OUT_PAL_YCBCR:
95462306a36Sopenharmony_ci	case PS3_PARAM_AV_MULTI_OUT_SECAM:
95562306a36Sopenharmony_ci		ps3av->region = PS3AV_REGION_50;
95662306a36Sopenharmony_ci		break;
95762306a36Sopenharmony_ci	case PS3_PARAM_AV_MULTI_OUT_PAL_RGB:
95862306a36Sopenharmony_ci		ps3av->region = PS3AV_REGION_50 | PS3AV_REGION_RGB;
95962306a36Sopenharmony_ci		break;
96062306a36Sopenharmony_ci	default:
96162306a36Sopenharmony_ci		ps3av->region = PS3AV_REGION_60;
96262306a36Sopenharmony_ci		break;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* init avsetting modules */
96662306a36Sopenharmony_ci	res = ps3av_cmd_init();
96762306a36Sopenharmony_ci	if (res < 0)
96862306a36Sopenharmony_ci		printk(KERN_ERR "%s: ps3av_cmd_init failed %d\n", __func__,
96962306a36Sopenharmony_ci		       res);
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	ps3av_get_hw_conf(ps3av);
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	mode_option = video_get_options(NULL);
97462306a36Sopenharmony_ci	if (mode_option && !strcmp(mode_option, "safe"))
97562306a36Sopenharmony_ci		safe_mode = 1;
97662306a36Sopenharmony_ci	id = ps3av_auto_videomode(&ps3av->av_hw_conf);
97762306a36Sopenharmony_ci	if (id < 0) {
97862306a36Sopenharmony_ci		printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);
97962306a36Sopenharmony_ci		res = -EINVAL;
98062306a36Sopenharmony_ci		goto fail;
98162306a36Sopenharmony_ci	}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	safe_mode = 0;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	mutex_lock(&ps3av->mutex);
98662306a36Sopenharmony_ci	ps3av->ps3av_mode = id;
98762306a36Sopenharmony_ci	mutex_unlock(&ps3av->mutex);
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	return 0;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cifail:
99462306a36Sopenharmony_ci	kfree(ps3av);
99562306a36Sopenharmony_ci	ps3av = NULL;
99662306a36Sopenharmony_ci	return res;
99762306a36Sopenharmony_ci}
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_cistatic int ps3av_remove(struct ps3_system_bus_device *dev)
100062306a36Sopenharmony_ci{
100162306a36Sopenharmony_ci	dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
100262306a36Sopenharmony_ci	if (ps3av) {
100362306a36Sopenharmony_ci		ps3av_cmd_fin();
100462306a36Sopenharmony_ci		flush_work(&ps3av->work);
100562306a36Sopenharmony_ci		kfree(ps3av);
100662306a36Sopenharmony_ci		ps3av = NULL;
100762306a36Sopenharmony_ci	}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
101062306a36Sopenharmony_ci	return 0;
101162306a36Sopenharmony_ci}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_cistatic void ps3av_shutdown(struct ps3_system_bus_device *dev)
101462306a36Sopenharmony_ci{
101562306a36Sopenharmony_ci	dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
101662306a36Sopenharmony_ci	ps3av_remove(dev);
101762306a36Sopenharmony_ci	dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
101862306a36Sopenharmony_ci}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_cistatic struct ps3_vuart_port_driver ps3av_driver = {
102162306a36Sopenharmony_ci	.core.match_id = PS3_MATCH_ID_AV_SETTINGS,
102262306a36Sopenharmony_ci	.core.core.name = "ps3_av",
102362306a36Sopenharmony_ci	.probe = ps3av_probe,
102462306a36Sopenharmony_ci	.remove = ps3av_remove,
102562306a36Sopenharmony_ci	.shutdown = ps3av_shutdown,
102662306a36Sopenharmony_ci};
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int __init ps3av_module_init(void)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	int error;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (!firmware_has_feature(FW_FEATURE_PS3_LV1))
103362306a36Sopenharmony_ci		return -ENODEV;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	pr_debug(" -> %s:%d\n", __func__, __LINE__);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	error = ps3_vuart_port_driver_register(&ps3av_driver);
103862306a36Sopenharmony_ci	if (error) {
103962306a36Sopenharmony_ci		printk(KERN_ERR
104062306a36Sopenharmony_ci		       "%s: ps3_vuart_port_driver_register failed %d\n",
104162306a36Sopenharmony_ci		       __func__, error);
104262306a36Sopenharmony_ci		return error;
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	pr_debug(" <- %s:%d\n", __func__, __LINE__);
104662306a36Sopenharmony_ci	return error;
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_cistatic void __exit ps3av_module_exit(void)
105062306a36Sopenharmony_ci{
105162306a36Sopenharmony_ci	pr_debug(" -> %s:%d\n", __func__, __LINE__);
105262306a36Sopenharmony_ci	ps3_vuart_port_driver_unregister(&ps3av_driver);
105362306a36Sopenharmony_ci	pr_debug(" <- %s:%d\n", __func__, __LINE__);
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cisubsys_initcall(ps3av_module_init);
105762306a36Sopenharmony_cimodule_exit(ps3av_module_exit);
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
106062306a36Sopenharmony_ciMODULE_DESCRIPTION("PS3 AV Settings Driver");
106162306a36Sopenharmony_ciMODULE_AUTHOR("Sony Computer Entertainment Inc.");
106262306a36Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_AV_SETTINGS);
1063