// SPDX-License-Identifier: GPL-2.0-only
/*
* PS3 AV backend support.
*
* Copyright (C) 2007 Sony Computer Entertainment Inc.
* Copyright 2007 Sony Corp.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/notifier.h>
#include <linux/ioctl.h>
#include <linux/slab.h>
#include <asm/firmware.h>
#include <asm/ps3av.h>
#include <asm/ps3.h>
#include <video/cmdline.h>
#include "vuart.h"
#define BUFSIZE 4096 /* vuart buf size */
#define PS3AV_BUF_SIZE 512 /* max packet size */
static int safe_mode;
static int timeout = 5000; /* in msec ( 5 sec ) */
module_param(timeout, int, 0644);
static struct ps3av {
struct mutex mutex;
struct work_struct work;
struct completion done;
int open_count;
struct ps3_system_bus_device *dev;
int region;
struct ps3av_pkt_av_get_hw_conf av_hw_conf;
u32 av_port[PS3AV_AV_PORT_MAX + PS3AV_OPT_PORT_MAX];
u32 opt_port[PS3AV_OPT_PORT_MAX];
u32 head[PS3AV_HEAD_MAX];
u32 audio_port;
int ps3av_mode;
int ps3av_mode_old;
union {
struct ps3av_reply_hdr reply_hdr;
u8 raw[PS3AV_BUF_SIZE];
} recv_buf;
} *ps3av;
/* color space */
#define YUV444 PS3AV_CMD_VIDEO_CS_YUV444_8
#define RGB8 PS3AV_CMD_VIDEO_CS_RGB_8
/* format */
#define XRGB PS3AV_CMD_VIDEO_FMT_X8R8G8B8
/* aspect */
#define A_N PS3AV_CMD_AV_ASPECT_4_3
#define A_W PS3AV_CMD_AV_ASPECT_16_9
static const struct avset_video_mode {
u32 cs;
u32 fmt;
u32 vid;
u32 aspect;
u32 x;
u32 y;
} video_mode_table[] = {
{ 0, }, /* auto */
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I, A_N, 720, 480},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P, A_N, 720, 480},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ, A_W, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I, A_N, 720, 576},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P, A_N, 720, 576},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ, A_W, 1280, 720},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080},
{YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080},
{ RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA, A_W, 1280, 768},
{ RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA, A_N, 1280, 1024},
{ RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA, A_W, 1920, 1200},
};
/* supported CIDs */
static u32 cmd_table[] = {
/* init */
PS3AV_CID_AV_INIT,
PS3AV_CID_AV_FIN,
PS3AV_CID_VIDEO_INIT,
PS3AV_CID_AUDIO_INIT,
/* set */
PS3AV_CID_AV_ENABLE_EVENT,
PS3AV_CID_AV_DISABLE_EVENT,
PS3AV_CID_AV_VIDEO_CS,
PS3AV_CID_AV_VIDEO_MUTE,
PS3AV_CID_AV_VIDEO_DISABLE_SIG,
PS3AV_CID_AV_AUDIO_PARAM,
PS3AV_CID_AV_AUDIO_MUTE,
PS3AV_CID_AV_HDMI_MODE,
PS3AV_CID_AV_TV_MUTE,
PS3AV_CID_VIDEO_MODE,
PS3AV_CID_VIDEO_FORMAT,
PS3AV_CID_VIDEO_PITCH,
PS3AV_CID_AUDIO_MODE,
PS3AV_CID_AUDIO_MUTE,
PS3AV_CID_AUDIO_ACTIVE,
PS3AV_CID_AUDIO_INACTIVE,
PS3AV_CID_AVB_PARAM,
/* get */
PS3AV_CID_AV_GET_HW_CONF,
PS3AV_CID_AV_GET_MONITOR_INFO,
/* event */
PS3AV_CID_EVENT_UNPLUGGED,
PS3AV_CID_EVENT_PLUGGED,
PS3AV_CID_EVENT_HDCP_DONE,
PS3AV_CID_EVENT_HDCP_FAIL,
PS3AV_CID_EVENT_HDCP_AUTH,
PS3AV_CID_EVENT_HDCP_ERROR,
0
};
#define PS3AV_EVENT_CMD_MASK 0x10000000
#define PS3AV_EVENT_ID_MASK 0x0000ffff
#define PS3AV_CID_MASK 0xffffffff
#define PS3AV_REPLY_BIT 0x80000000
#define ps3av_event_get_port_id(cid) ((cid >> 16) & 0xff)
static u32 *ps3av_search_cmd_table(u32 cid, u32 mask)
{
u32 *table;
int i;
table = cmd_table;
for (i = 0;; table++, i++) {
if ((*table & mask) == (cid & mask))
break;
if (*table == 0)
return NULL;
}
return table;
}
static int ps3av_parse_event_packet(const struct ps3av_reply_hdr *hdr)
{
u32 *table;
if (hdr->cid & PS3AV_EVENT_CMD_MASK) {
table = ps3av_search_cmd_table(hdr->cid, PS3AV_EVENT_CMD_MASK);
if (table)
dev_dbg(&ps3av->dev->core,
"recv event packet cid:%08x port:0x%x size:%d\n",
hdr->cid, ps3av_event_get_port_id(hdr->cid),
hdr->size);
else
printk(KERN_ERR
"%s: failed event packet, cid:%08x size:%d\n",
__func__, hdr->cid, hdr->size);
return 1; /* receive event packet */
}
return 0;
}
#define POLLING_INTERVAL 25 /* in msec */
static int ps3av_vuart_write(struct ps3_system_bus_device *dev,
const void *buf, unsigned long size)
{
int error;
dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
error = ps3_vuart_write(dev, buf, size);
dev_dbg(&dev->core, " <- %s:%d\n", __func__, __LINE__);
return error ? error : size;
}
static int ps3av_vuart_read(struct ps3_system_bus_device *dev, void *