544 lines
12 KiB
C
544 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2019 MediaTek Inc.
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
#include <linux/mm.h>
|
|
#include <linux/init.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/device.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/miscdevice.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/cdev.h>
|
|
#include <linux/module.h>
|
|
#include <linux/list.h>
|
|
#include <linux/types.h>
|
|
#include <linux/fb.h>
|
|
|
|
#include <linux/uaccess.h>
|
|
#include <linux/atomic.h>
|
|
|
|
#include "extd_log.h"
|
|
#include "extd_utils.h"
|
|
#include "extd_factory.h"
|
|
#include "mtk_extd_mgr.h"
|
|
#include <linux/suspend.h>
|
|
#include "extd_platform.h"
|
|
|
|
|
|
#define EXTD_DEVNAME "hdmitx"
|
|
#define EXTD_DEV_ID(id) (((id)>>16)&0x0ff)
|
|
#define EXTD_DEV_PARAM(id) ((id)&0x0ff)
|
|
|
|
|
|
static dev_t extd_devno;
|
|
static struct cdev *extd_cdev;
|
|
static struct class *extd_class;
|
|
|
|
static const struct EXTD_DRIVER *extd_driver[DEV_MAX_NUM];
|
|
static const struct EXTD_DRIVER *extd_factory_driver[DEV_MAX_NUM - 1];
|
|
|
|
static void external_display_enable(unsigned long param)
|
|
{
|
|
enum EXTD_DEV_ID device_id = EXTD_DEV_ID(param);
|
|
int enable = EXTD_DEV_PARAM(param);
|
|
|
|
if (device_id >= DEV_MAX_NUM) {
|
|
EXTDERR("%s, device id is invalid!", __func__);
|
|
return;
|
|
}
|
|
|
|
if (extd_driver[device_id] && extd_driver[device_id]->enable)
|
|
extd_driver[device_id]->enable(enable);
|
|
}
|
|
|
|
static void external_display_power_enable(unsigned long param)
|
|
{
|
|
enum EXTD_DEV_ID device_id = EXTD_DEV_ID(param);
|
|
int enable = EXTD_DEV_PARAM(param);
|
|
|
|
if (device_id >= DEV_MAX_NUM) {
|
|
EXTDERR
|
|
("%s, device id is invalid!", __func__);
|
|
return;
|
|
}
|
|
|
|
if (extd_driver[device_id] && extd_driver[device_id]->power_enable)
|
|
extd_driver[device_id]->power_enable(enable);
|
|
}
|
|
|
|
static void external_display_set_resolution(unsigned long param)
|
|
{
|
|
enum EXTD_DEV_ID device_id = EXTD_DEV_ID(param);
|
|
int res = EXTD_DEV_PARAM(param);
|
|
|
|
if (device_id >= DEV_MAX_NUM) {
|
|
EXTDERR
|
|
("%s, device id is invalid!", __func__);
|
|
return;
|
|
}
|
|
|
|
if (extd_driver[device_id] && extd_driver[device_id]->set_resolution)
|
|
extd_driver[device_id]->set_resolution(res);
|
|
}
|
|
|
|
static int external_display_get_dev_info(unsigned long param, void *info)
|
|
{
|
|
int ret = 0;
|
|
enum EXTD_DEV_ID device_id = EXTD_DEV_ID(param);
|
|
|
|
if (device_id >= DEV_MAX_NUM) {
|
|
EXTDERR
|
|
("%s, device id is invalid!", __func__);
|
|
return ret;
|
|
}
|
|
|
|
if (extd_driver[device_id] && extd_driver[device_id]->get_dev_info)
|
|
ret = extd_driver[device_id]->get_dev_info(AP_GET_INFO, info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int external_display_get_capability(unsigned long param, void *info)
|
|
{
|
|
int ret = 0;
|
|
enum EXTD_DEV_ID device_id = EXTD_DEV_ID(param);
|
|
|
|
device_id = DEV_MHL;
|
|
|
|
if (device_id >= DEV_MAX_NUM) {
|
|
EXTDERR
|
|
("%s, device id is invalid!", __func__);
|
|
return ret;
|
|
}
|
|
|
|
if (extd_driver[device_id] && extd_driver[device_id]->get_capability)
|
|
ret = extd_driver[device_id]->get_capability(info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static long mtk_extd_mgr_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
void __user *argp = (void __user *)arg;
|
|
int r = 0;
|
|
|
|
#ifndef MTK_EXTENSION_MODE_SUPPORT
|
|
EXTDINFO("[EXTD]ioctl= %s(%d), arg = %lu\n", _extd_ioctl_spy(cmd),
|
|
cmd & 0xff, arg);
|
|
#endif
|
|
|
|
switch (cmd) {
|
|
case MTK_HDMI_AUDIO_VIDEO_ENABLE:
|
|
{
|
|
external_display_enable(arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_POWER_ENABLE:
|
|
{
|
|
external_display_power_enable(arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_VIDEO_CONFIG:
|
|
{
|
|
external_display_set_resolution(arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_FORCE_FULLSCREEN_ON:
|
|
{
|
|
arg = arg | 0x1;
|
|
external_display_power_enable(arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_FORCE_FULLSCREEN_OFF:
|
|
{
|
|
arg = arg & 0x0FF0000;
|
|
external_display_power_enable(arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_GET_DEV_INFO:
|
|
{
|
|
int displayid = 0;
|
|
|
|
if (copy_from_user(&displayid, argp,
|
|
sizeof(displayid))) {
|
|
EXTDERR(": copy_from_user failed! line:%d\n",
|
|
__LINE__);
|
|
return -EAGAIN;
|
|
}
|
|
r = external_display_get_dev_info(displayid, argp);
|
|
break;
|
|
}
|
|
case MTK_HDMI_USBOTG_STATUS:
|
|
{
|
|
break;
|
|
}
|
|
case MTK_HDMI_AUDIO_ENABLE:
|
|
{
|
|
EXTDINFO("[EXTD]hdmi_set_audio_enable, arg = %lu\n",
|
|
arg);
|
|
if (extd_driver[DEV_MHL]
|
|
&& extd_driver[DEV_MHL]->set_audio_enable) {
|
|
arg = arg & 0x0FF;
|
|
extd_driver[DEV_MHL]->set_audio_enable(arg);
|
|
break;
|
|
}
|
|
}
|
|
case MTK_HDMI_VIDEO_ENABLE:
|
|
{
|
|
break;
|
|
}
|
|
case MTK_HDMI_AUDIO_CONFIG:
|
|
{
|
|
EXTDINFO("[EXTD]hdmi_audio_format, arg = %lu\n",
|
|
arg);
|
|
if (extd_driver[DEV_MHL]
|
|
&& extd_driver[DEV_MHL]->set_audio_format)
|
|
extd_driver[DEV_MHL]->set_audio_format(arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_IS_FORCE_AWAKE:
|
|
{
|
|
|
|
#ifdef CONFIG_MTK_INTERNAL_HDMI_SUPPORT
|
|
r = hdmi_is_force_awake(argp);
|
|
#endif
|
|
break;
|
|
}
|
|
case MTK_HDMI_GET_EDID:
|
|
{
|
|
if (extd_driver[DEV_MHL]
|
|
&& extd_driver[DEV_MHL]->get_edid)
|
|
r = extd_driver[DEV_MHL]->get_edid(argp);
|
|
break;
|
|
}
|
|
case MTK_HDMI_GET_CAPABILITY:
|
|
{
|
|
r = external_display_get_capability(
|
|
*((unsigned long *)argp), argp);
|
|
break;
|
|
}
|
|
case MTK_HDMI_SCREEN_CAPTURE:
|
|
{
|
|
break;
|
|
}
|
|
case MTK_HDMI_FACTORY_CHIP_INIT:
|
|
{
|
|
if (extd_factory_driver[DEV_MHL]
|
|
&& extd_factory_driver[DEV_MHL]->factory_mode_test)
|
|
r = extd_factory_driver[DEV_MHL]->factory_mode_test(
|
|
STEP1_CHIP_INIT, NULL);
|
|
break;
|
|
}
|
|
case MTK_HDMI_FACTORY_JUDGE_CALLBACK:
|
|
{
|
|
if (extd_factory_driver[DEV_MHL]
|
|
&& extd_factory_driver[DEV_MHL]->factory_mode_test)
|
|
r = extd_factory_driver[DEV_MHL]->factory_mode_test(
|
|
STEP2_JUDGE_CALLBACK, argp);
|
|
break;
|
|
}
|
|
case MTK_HDMI_FACTORY_START_DPI_AND_CONFIG:
|
|
{
|
|
if (extd_factory_driver[DEV_MHL]
|
|
&& extd_factory_driver[DEV_MHL]->factory_mode_test)
|
|
r = extd_factory_driver[DEV_MHL]->factory_mode_test(
|
|
STEP3_START_DPI_AND_CONFIG, (void *)arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_FACTORY_DPI_STOP_AND_POWER_OFF:
|
|
{
|
|
if (extd_factory_driver[DEV_MHL]
|
|
&& extd_factory_driver[DEV_MHL]->factory_mode_test)
|
|
r = extd_factory_driver[DEV_MHL]->factory_mode_test(
|
|
STEP4_DPI_STOP_AND_POWER_OFF, (void *)arg);
|
|
break;
|
|
}
|
|
case MTK_HDMI_AUDIO_SETTING:
|
|
{
|
|
if (extd_driver[DEV_MHL]
|
|
&& extd_driver[DEV_MHL]->audio_setting)
|
|
r = extd_driver[DEV_MHL]->audio_setting(argp);
|
|
break;
|
|
}
|
|
case MTK_HDMI_HDCP_KEY:
|
|
{
|
|
if (extd_driver[DEV_MHL]
|
|
&& extd_driver[DEV_MHL]->install_hdcpkey)
|
|
r = extd_driver[DEV_MHL]->install_hdcpkey(argp);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
EXTDERR("[EXTD]ioctl(%d) arguments is not support\n",
|
|
cmd & 0x0ff);
|
|
r = -EFAULT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifndef MTK_EXTENSION_MODE_SUPPORT
|
|
EXTDINFO("[EXTD]ioctl = %s(%d) done\n", _extd_ioctl_spy(cmd),
|
|
cmd & 0x0ff);
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
static int mtk_extd_mgr_open(struct inode *inode, struct file *file)
|
|
{
|
|
/*EXTDFUNC();*/
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_extd_mgr_release(struct inode *inode, struct file *file)
|
|
{
|
|
/*EXTDFUNC();*/
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
static long mtk_extd_mgr_compat_ioctl(struct file *file, unsigned int cmd,
|
|
unsigned long arg)
|
|
{
|
|
long ret = -ENOIOCTLCMD;
|
|
|
|
switch (cmd) {
|
|
|
|
/* add cases here for 32bit/64bit conversion */
|
|
/* ... */
|
|
|
|
default:
|
|
ret = mtk_extd_mgr_ioctl(file, cmd, arg);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
const struct file_operations external_display_fops = {
|
|
.owner = THIS_MODULE,
|
|
.unlocked_ioctl = mtk_extd_mgr_ioctl,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = mtk_extd_mgr_compat_ioctl,
|
|
#endif
|
|
.open = mtk_extd_mgr_open,
|
|
.release = mtk_extd_mgr_release,
|
|
};
|
|
|
|
static const struct of_device_id extd_of_ids[] = {
|
|
{.compatible = "mediatek,extd_dev",},
|
|
{}
|
|
};
|
|
|
|
struct device *ext_dev_context;
|
|
static int mtk_extd_mgr_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
int i = 0;
|
|
struct class_device *class_dev = NULL;
|
|
|
|
EXTDFUNC();
|
|
|
|
/* Allocate device number for hdmi driver */
|
|
ret = alloc_chrdev_region(&extd_devno, 0, 1, EXTD_DEVNAME);
|
|
|
|
if (ret) {
|
|
EXTDERR("alloc_chrdev_region fail\n");
|
|
return -1;
|
|
}
|
|
|
|
extd_cdev = cdev_alloc();
|
|
extd_cdev->owner = THIS_MODULE;
|
|
extd_cdev->ops = &external_display_fops;
|
|
ret = cdev_add(extd_cdev, extd_devno, 1);
|
|
|
|
extd_class = class_create(THIS_MODULE, EXTD_DEVNAME);
|
|
/* mknod /dev/hdmitx */
|
|
class_dev =
|
|
(struct class_device *)device_create(extd_class, NULL, extd_devno,
|
|
NULL, EXTD_DEVNAME);
|
|
ext_dev_context = (struct device *)&(pdev->dev);
|
|
|
|
for (i = DEV_MHL; i < DEV_MAX_NUM; i++) {
|
|
if (extd_driver[i] != 0 && extd_driver[i]->post_init != 0)
|
|
extd_driver[i]->post_init();
|
|
}
|
|
|
|
EXTDINFO("[%s] out\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int mtk_extd_mgr_remove(struct platform_device *pdev)
|
|
{
|
|
EXTDFUNC();
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM
|
|
int extd_pm_suspend(struct device *device)
|
|
{
|
|
EXTDFUNC();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int extd_pm_resume(struct device *device)
|
|
{
|
|
EXTDFUNC();
|
|
|
|
return 0;
|
|
}
|
|
|
|
const struct dev_pm_ops extd_pm_ops = {
|
|
.suspend = extd_pm_suspend,
|
|
.resume = extd_pm_resume,
|
|
};
|
|
#endif
|
|
|
|
static struct platform_driver external_display_driver = {
|
|
.probe = mtk_extd_mgr_probe,
|
|
.remove = mtk_extd_mgr_remove,
|
|
.driver = {
|
|
.name = EXTD_DEVNAME,
|
|
#ifdef CONFIG_PM
|
|
.pm = &extd_pm_ops,
|
|
#endif
|
|
.of_match_table = extd_of_ids,
|
|
}
|
|
};
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
static void extd_early_suspend(struct early_suspend *h)
|
|
{
|
|
EXTDFUNC();
|
|
int i = 0;
|
|
|
|
for (i = DEV_MHL; i < DEV_MAX_NUM - 1; i++) {
|
|
if (i != DEV_EINK && extd_driver[i]
|
|
&& extd_driver[i]->power_enable)
|
|
extd_driver[i]->power_enable(0);
|
|
}
|
|
}
|
|
|
|
static void extd_late_resume(struct early_suspend *h)
|
|
{
|
|
EXTDFUNC();
|
|
int i = 0;
|
|
|
|
for (i = DEV_MHL; i < DEV_MAX_NUM - 1; i++) {
|
|
if (i != DEV_EINK && extd_driver[i]
|
|
&& extd_driver[i]->power_enable)
|
|
extd_driver[i]->power_enable(1);
|
|
}
|
|
}
|
|
|
|
static struct early_suspend extd_early_suspend_handler = {
|
|
.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1,
|
|
.suspend = extd_early_suspend,
|
|
.resume = extd_late_resume,
|
|
};
|
|
#endif
|
|
|
|
#if defined(CONFIG_MTK_HDMI_SUPPORT)
|
|
static int fb_notifier_callback(struct notifier_block *p,
|
|
unsigned long event, void *data)
|
|
{
|
|
int i = 0;
|
|
int blank_mode = 0;
|
|
struct fb_event *evdata = data;
|
|
|
|
if (event != FB_EARLY_EVENT_BLANK)
|
|
return 0;
|
|
|
|
blank_mode = *(int *)evdata->data;
|
|
EXTDMSG("[%s] - blank_mode:%d\n", __func__, blank_mode);
|
|
|
|
switch (blank_mode) {
|
|
case FB_BLANK_UNBLANK:
|
|
case FB_BLANK_NORMAL:
|
|
for (i = DEV_MHL; i < DEV_MAX_NUM; i++) {
|
|
if (i != DEV_EINK && extd_driver[i]
|
|
&& extd_driver[i]->power_enable)
|
|
extd_driver[i]->power_enable(1);
|
|
}
|
|
|
|
break;
|
|
case FB_BLANK_POWERDOWN:
|
|
for (i = DEV_MHL; i < DEV_MAX_NUM; i++) {
|
|
if (i != DEV_EINK && extd_driver[i]
|
|
&& extd_driver[i]->power_enable)
|
|
extd_driver[i]->power_enable(0);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
EXTDERR("[%s] - unknown blank mode!\n", __func__);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if defined(CONFIG_MTK_HDMI_SUPPORT)
|
|
static struct notifier_block notifier;
|
|
#endif
|
|
static int __init mtk_extd_mgr_init(void)
|
|
{
|
|
int i = 0;
|
|
#if defined(CONFIG_MTK_HDMI_SUPPORT)
|
|
int ret = 0;
|
|
#endif
|
|
/* struct notifier_block notifier;*/
|
|
|
|
EXTDFUNC();
|
|
|
|
extd_driver[DEV_MHL] = EXTD_HDMI_Driver();
|
|
extd_driver[DEV_EINK] = EXTD_EPD_Driver();
|
|
#if defined(CONFIG_MTK_DUAL_DISPLAY_SUPPORT) && \
|
|
(CONFIG_MTK_DUAL_DISPLAY_SUPPORT == 2)
|
|
extd_driver[DEV_LCM] = EXTD_LCM_Driver();
|
|
#endif
|
|
extd_factory_driver[DEV_MHL] = EXTD_Factory_HDMI_Driver();
|
|
|
|
for (i = DEV_MHL; i < DEV_MAX_NUM; i++) {
|
|
if (extd_driver[i] && extd_driver[i]->init)
|
|
extd_driver[i]->init();
|
|
}
|
|
|
|
if (platform_driver_register(&external_display_driver)) {
|
|
EXTDERR("failed to register mtkfb driver\n");
|
|
return -1;
|
|
}
|
|
#if defined(CONFIG_MTK_HDMI_SUPPORT)
|
|
notifier.notifier_call = fb_notifier_callback;
|
|
ret = fb_register_client(¬ifier);
|
|
if (ret)
|
|
EXTDERR("unable to register fb callback!\n");
|
|
#endif
|
|
|
|
#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
register_early_suspend(&extd_early_suspend_handler);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void __exit mtk_extd_mgr_exit(void)
|
|
{
|
|
device_destroy(extd_class, extd_devno);
|
|
class_destroy(extd_class);
|
|
cdev_del(extd_cdev);
|
|
unregister_chrdev_region(extd_devno, 1);
|
|
}
|
|
|
|
late_initcall(mtk_extd_mgr_init);
|
|
module_exit(mtk_extd_mgr_exit);
|
|
MODULE_AUTHOR("www.mediatek.com>");
|
|
MODULE_DESCRIPTION("External Display Driver");
|
|
MODULE_LICENSE("GPL");
|