static DEVICE_ATTR_RW(debug);
+static ssize_t prop_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rk_display_device *dsp = dev_get_drvdata(dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&dsp->lock);
+ if (dsp->ops && dsp->ops->getvrinfo)
+ ret = dsp->ops->getvrinfo(dsp, buf);
+ mutex_unlock(&dsp->lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RO(prop);
+
static struct attribute *display_device_attrs[] = {
&dev_attr_name.attr,
&dev_attr_type.attr,
&dev_attr_audioinfo.attr,
&dev_attr_monspecs.attr,
&dev_attr_debug.attr,
+ &dev_attr_prop.attr,
NULL,
};
#include "rockchip_dp.h"
#include <linux/delay.h>
+#include <linux/of_gpio.h>
static int rockchip_dp_removed(struct hdmi *hdmi_drv)
{
return NOTIFY_OK;
}
+static int cdn_dp_get_prop_dts(struct hdmi *hdmi, struct device_node *np)
+{
+ const struct property *prop;
+ int i = 0, nstates = 0;
+ const __be32 *val;
+ int value;
+ struct edid_prop_value *pval = NULL;
+
+ if (!hdmi || !np)
+ return -EINVAL;
+
+ if (!of_property_read_u32(np, "dp_edid_auto_support", &value))
+ hdmi->edid_auto_support = value;
+
+ prop = of_find_property(np, "dp_edid_prop_value", NULL);
+ if (!prop || !prop->value) {
+ pr_info("%s:No edid-prop-value, %d\n", __func__, !prop);
+ return -EINVAL;
+ }
+
+ nstates = (prop->length / sizeof(struct edid_prop_value));
+ pval = kcalloc(nstates, sizeof(struct edid_prop_value), GFP_NOWAIT);
+ if (!pval)
+ return -ENOMEM;
+
+ for (i = 0, val = prop->value; i < nstates; i++) {
+ pval[i].vid = be32_to_cpup(val++);
+ pval[i].pid = be32_to_cpup(val++);
+ pval[i].sn = be32_to_cpup(val++);
+ pval[i].xres = be32_to_cpup(val++);
+ pval[i].yres = be32_to_cpup(val++);
+ pval[i].vic = be32_to_cpup(val++);
+ pval[i].width = be32_to_cpup(val++);
+ pval[i].height = be32_to_cpup(val++);
+ pval[i].x_w = be32_to_cpup(val++);
+ pval[i].x_h = be32_to_cpup(val++);
+ pval[i].hwrotation = be32_to_cpup(val++);
+ pval[i].einit = be32_to_cpup(val++);
+ pval[i].vsync = be32_to_cpup(val++);
+ pval[i].panel = be32_to_cpup(val++);
+ pval[i].scan = be32_to_cpup(val++);
+
+ pr_info("%s: 0x%x 0x%x 0x%x %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ __func__, pval[i].vid, pval[i].pid, pval[i].sn,
+ pval[i].width, pval[i].height, pval[i].xres,
+ pval[i].yres, pval[i].vic, pval[i].x_w,
+ pval[i].x_h, pval[i].hwrotation, pval[i].einit,
+ pval[i].vsync, pval[i].panel, pval[i].scan);
+ }
+
+ hdmi->pvalue = pval;
+ hdmi->nstates = nstates;
+
+ return 0;
+}
+
int cdn_dp_fb_register(struct platform_device *pdev, void *dp)
{
struct hdmi_ops *rk_dp_ops;
dp_dev->hdmi->colormode = HDMI_COLOR_RGB_0_255;
dp_dev->dp = dp;
+ cdn_dp_get_prop_dts(dp_dev->hdmi, np);
dp_dev->fb_notif.notifier_call = rockchip_dp_fb_event_notify;
fb_register_client(&dp_dev->fb_notif);
dev_set_drvdata(dev, dp_dev);
video->color_input = HDMI_COLOR_RGB_0_255;
if ((hdmi->vic & HDMI_VIDEO_DMT) || (hdmi->vic & HDMI_VIDEO_DISCRETE_VR)) {
- video->vic = hdmi->vic;
+ if (hdmi->edid_auto_support) {
+ if (hdmi->prop.value.vic)
+ video->vic = hdmi->prop.value.vic;
+ else
+ video->vic = hdmi->vic;
+ } else {
+ video->vic = hdmi->vic;
+ }
video->color_output_depth = 8;
} else {
video->vic = hdmi->vic & HDMI_VIC_MASK;
continue;
}
- rc = hdmi_edid_parse_base(pedid->raw[0], &extendblock, pedid);
+ rc = hdmi_edid_parse_base(hdmi,
+ pedid->raw[0], &extendblock, pedid);
if (rc) {
dev_err(hdmi->dev,
"[HDMI] parse edid base block error\n");
return E_HDMI_EDID_SUCCESS;
}
-int hdmi_edid_parse_base(unsigned char *buf,
+static int edid_parse_prop_value(unsigned char *buf,
+ struct hdmi_edid *pedid)
+{
+ unsigned char *block = &buf[0x36];
+
+ pedid->value.vid = ((buf[ID_MANUFACTURER_NAME_END] << 8) |
+ (buf[ID_MANUFACTURER_NAME]));
+ pedid->value.pid = ((buf[ID_MODEL + 1] << 8) |
+ (buf[ID_MODEL]));
+ pedid->value.sn = ((buf[ID_SERIAL_NUMBER + 3] << 24) |
+ (buf[ID_SERIAL_NUMBER + 2] << 16) |
+ (buf[ID_SERIAL_NUMBER + 1] << 8) |
+ buf[ID_SERIAL_NUMBER]);
+ pedid->value.xres = H_ACTIVE;
+ pedid->value.yres = V_ACTIVE;
+
+ pr_info("%s:read:vid=0x%x,pid=0x%x,sn=0x%x,xres=%d,yres=%d\n",
+ __func__, pedid->value.vid, pedid->value.pid,
+ pedid->value.sn, pedid->value.xres, pedid->value.yres);
+
+ return 0;
+}
+
+int hdmi_edid_parse_base(struct hdmi *hdmi, unsigned char *buf,
int *extend_num, struct hdmi_edid *pedid)
{
int rc = E_HDMI_EDID_SUCCESS;
fb_edid_to_monspecs(buf, pedid->specs);
+ if (hdmi->edid_auto_support)
+ edid_parse_prop_value(buf, pedid);
+
out:
/* For some sink, edid checksum is failed because several
* byte is wrong. To fix this case, we think it is a good
.pixelrepeat = 1,
.interface = OUT_P888,
},
+ {
+ /* samsung */
+ .mode = {
+ .name = "1440x2560@70Hz",
+ .refresh = 70,
+ .xres = 1440,
+ .yres = 2560,
+ .pixclock = 285000000,
+ .left_margin = 40,
+ .right_margin = 80,
+ .upper_margin = 2,
+ .lower_margin = 6,
+ .hsync_len = 20,
+ .vsync_len = 8,
+ .sync = 0,
+ .vmode = 0,
+ .flag = 0,
+ },
+ .vic = HDMI_VIDEO_DISCRETE_VR | 6,
+ .vic_2nd = 0,
+ .pixelrepeat = 1,
+ .interface = OUT_P888,
+ },
};
static int hdmi_set_info(struct rk_screen *screen, struct hdmi *hdmi)
if (hdmi->vic == 0)
hdmi->vic = hdmi->property->defaultmode;
- if ((hdmi->vic & HDMI_VIDEO_DMT) || (hdmi->vic & HDMI_VIDEO_DISCRETE_VR))
- vic = hdmi->vic;
- else
- vic = hdmi->vic & HDMI_VIC_MASK;
+ if (hdmi->edid_auto_support) {
+ if ((hdmi->vic & HDMI_VIDEO_DMT) ||
+ (hdmi->vic & HDMI_VIDEO_DISCRETE_VR)) {
+ if (hdmi->prop.value.vic)
+ vic = hdmi->prop.value.vic;
+ else
+ vic = hdmi->vic;
+ } else {
+ vic = hdmi->vic & HDMI_VIC_MASK;
+ }
+ } else {
+ if ((hdmi->vic & HDMI_VIDEO_DMT) ||
+ (hdmi->vic & HDMI_VIDEO_DISCRETE_VR))
+ vic = hdmi->vic;
+ else
+ vic = hdmi->vic & HDMI_VIC_MASK;
+ }
+
for (i = 0; i < ARRAY_SIZE(hdmi_mode); i++) {
if (hdmi_mode[i].vic == vic ||
hdmi_mode[i].vic_2nd == vic)
screen->overscan.top = hdmi->yscale;
screen->overscan.right = hdmi->xscale;
screen->overscan.bottom = hdmi->yscale;
+
+ screen->width = hdmi->prop.value.width;
+ screen->height = hdmi->prop.value.height;
+ pr_info("%s:line=%d %d %d %d %d %d %d %d %d\n",
+ __func__, __LINE__, screen->mode.xres, screen->mode.yres,
+ screen->mode.left_margin, screen->mode.right_margin,
+ screen->mode.upper_margin, screen->mode.lower_margin,
+ screen->mode.hsync_len, screen->mode.vsync_len);
+
return 0;
}
}
}
+static int edid_select_prop_value(struct hdmi *hdmi)
+{
+ struct edid_prop_value *prop_value = NULL;
+ int nstates = 0;
+ int i, vid, pid, sn, xres, yres, reboot = 0;
+
+ prop_value = hdmi->pvalue;
+ nstates = hdmi->nstates;
+
+ if (!prop_value) {
+ pr_info("%s:pvalue is NULL\n", __func__);
+ return -1;
+ }
+
+ vid = hdmi->edid.value.vid;
+ pid = hdmi->edid.value.pid;
+ sn = hdmi->edid.value.sn;
+ xres = hdmi->edid.value.xres;
+ yres = hdmi->edid.value.yres;
+
+ for (i = 0; i < nstates; i++) {
+ if ((prop_value[i].vid == vid) &&
+ (prop_value[i].pid == pid) &&
+ (prop_value[i].sn == sn) &&
+ (prop_value[i].xres == xres) &&
+ (prop_value[i].yres == yres)) {
+ hdmi->edid.value = prop_value[i];
+ hdmi->prop.value = prop_value[i];
+ if ((hdmi->prop.valid) &&
+ ((hdmi->prop.last_vid != vid) ||
+ (hdmi->prop.last_pid != pid) ||
+ (hdmi->prop.last_sn != sn) ||
+ (hdmi->prop.last_xres != xres) ||
+ (hdmi->prop.last_yres != yres))) {
+ reboot = 1;
+ } else {
+ reboot = 0;
+ }
+
+ hdmi->prop.last_vid = vid;
+ hdmi->prop.last_pid = pid;
+ hdmi->prop.last_sn = sn;
+ hdmi->prop.last_xres = xres;
+ hdmi->prop.last_yres = yres;
+ hdmi->prop.valid = 1;
+ pr_info("%s:i=%d reboot=%d,valid=%d\n",
+ __func__, i, reboot, hdmi->prop.valid);
+
+ break;
+ }
+ }
+
+ if (reboot) {
+ dev_info(hdmi->dev, "%s:kernel_restart\n", __func__);
+ kernel_restart(NULL);
+ }
+
+ return 0;
+}
+
/**
* hdmi_ouputmode_select - select hdmi transmitter output mode: hdmi or dvi?
* @hdmi: handle of hdmi
hdmi->edid.ycbcr422 = 0;
}
+ if (hdmi->edid_auto_support)
+ edid_select_prop_value(hdmi);
+
if (head->next == head) {
dev_info(hdmi->dev,
"warning: no CEA video mode parsed from EDID !!!!\n");
return len;
}
+static int vr_get_info(struct rk_display_device *device, char *buf)
+{
+ struct hdmi *hdmi = device->priv_data;
+ int valid, width, height, x_w, x_h, hwr, einit, vsync, panel, scan;
+ int len = 0;
+
+ valid = hdmi->prop.valid;
+ width = hdmi->prop.value.width;
+ height = hdmi->prop.value.height;
+ x_w = hdmi->prop.value.x_w;
+ x_h = hdmi->prop.value.x_h;
+ hwr = hdmi->prop.value.hwrotation;
+ einit = hdmi->prop.value.einit;
+ vsync = hdmi->prop.value.vsync;
+ panel = hdmi->prop.value.panel;
+ scan = hdmi->prop.value.scan;
+
+ len = snprintf(buf, PAGE_SIZE,
+ "valid=%d,width=%d,height=%d,xres=%d,yres=%d,hwrotation=%d,orientation=%d,vsync=%d,panel=%d,scan=%d\n",
+ valid, width, height, x_w, x_h, hwr, einit, vsync, panel, scan);
+
+ return len;
+}
+
static struct rk_display_ops hdmi_display_ops = {
.setenable = hdmi_set_enable,
.getenable = hdmi_get_enable,
.setscale = hdmi_set_scale,
.getscale = hdmi_get_scale,
.getdebug = hdmi_get_debug,
+ .getvrinfo = vr_get_info,
};
static int hdmi_display_probe(struct rk_display_device *device, void *devdata)
device->owner = THIS_MODULE;
strcpy(device->type, "HDMI");
+ if (strstr(hdmi->property->name, "dp"))
+ strcpy(device->type, "DP");
+ else
+ strcpy(device->type, "HDMI");
+
device->priority = DISPLAY_PRIORITY_HDMI;
device->name = hdmi->property->name;
device->property = hdmi->property->display;
#include <linux/switch.h>
#endif
#include <sound/pcm_params.h>
+#include <linux/reboot.h>
#define HDMI_VIDEO_NORMAL 0
#define HDMI_VIDEO_DMT BIT(9)
};
#define HDMI_MAX_EDID_BLOCK 8
+
+struct edid_prop_value {
+ int vid;
+ int pid;
+ int sn;
+ int xres;
+ int yres;
+ int vic;
+ int width;
+ int height;
+ int x_w;
+ int x_h;
+ int hwrotation;
+ int einit;
+ int vsync;
+ int panel;
+ int scan;
+};
+
+struct edid_prop_data {
+ struct edid_prop_value value;
+
+ int valid;
+ int last_vid;
+ int last_pid;
+ int last_sn;
+ int last_xres;
+ int last_yres;
+};
+
/* HDMI EDID Information */
struct hdmi_edid {
unsigned char sink_hdmi; /* HDMI display device flag */
unsigned char dual_view;
unsigned char osd_disparity_3d;
+ struct edid_prop_value value;
+
unsigned int colorimetry;
struct fb_monspecs *specs; /*Device spec*/
struct list_head modelist; /*Device supported display mode list*/
int colormode; /* Output color mode*/
int colorimetry; /* Output colorimetry */
struct hdmi_edid edid; /* EDID information*/
+ struct edid_prop_data prop; /* Property for dp */
+ struct edid_prop_value *pvalue;
+ int nstates;
+ int edid_auto_support; /* Auto dp enable flag */
+
int enable; /* Enable flag*/
int sleep; /* Sleep flag*/
int vic; /* HDMI output video information code*/
struct device *parent);
void hdmi_unregister_display_sysfs(struct hdmi *hdmi);
-int hdmi_edid_parse_base(unsigned char *buf,
+int hdmi_edid_parse_base(struct hdmi *hdmi, unsigned char *buf,
int *extend_num, struct hdmi_edid *pedid);
int hdmi_edid_parse_extensions(unsigned char *buf,
struct hdmi_edid *pedid);
{}
};
+static int hdmi_get_prop_dts(struct hdmi *hdmi, struct device_node *np)
+{
+ const struct property *prop;
+ int i = 0, nstates = 0;
+ const __be32 *val;
+ int value;
+ struct edid_prop_value *pval = NULL;
+
+ if (!hdmi || !np) {
+ pr_info("%s:line=%d hdmi or np is null\n", __func__, __LINE__);
+ return -1;
+ }
+
+ if (!of_property_read_u32(np, "hdmi_edid_auto_support", &value))
+ hdmi->edid_auto_support = value;
+
+ prop = of_find_property(np, "hdmi_edid_prop_value", NULL);
+ if (!prop || !prop->value) {
+ pr_info("%s:No edid-prop-value, %d\n", __func__, !prop);
+ return -1;
+ }
+
+ nstates = (prop->length / sizeof(struct edid_prop_value));
+ pval = kcalloc(nstates, sizeof(struct edid_prop_value), GFP_NOWAIT);
+
+ for (i = 0, val = prop->value; i < nstates; i++) {
+ pval[i].vid = be32_to_cpup(val++);
+ pval[i].pid = be32_to_cpup(val++);
+ pval[i].sn = be32_to_cpup(val++);
+ pval[i].xres = be32_to_cpup(val++);
+ pval[i].yres = be32_to_cpup(val++);
+ pval[i].vic = be32_to_cpup(val++);
+ pval[i].width = be32_to_cpup(val++);
+ pval[i].height = be32_to_cpup(val++);
+ pval[i].x_w = be32_to_cpup(val++);
+ pval[i].x_h = be32_to_cpup(val++);
+ pval[i].hwrotation = be32_to_cpup(val++);
+ pval[i].einit = be32_to_cpup(val++);
+ pval[i].vsync = be32_to_cpup(val++);
+ pval[i].panel = be32_to_cpup(val++);
+ pval[i].scan = be32_to_cpup(val++);
+
+ pr_info("%s: 0x%x 0x%x 0x%x %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ __func__, pval[i].vid, pval[i].pid, pval[i].sn,
+ pval[i].width, pval[i].height, pval[i].xres,
+ pval[i].yres, pval[i].vic, pval[i].x_w,
+ pval[i].x_h, pval[i].hwrotation, pval[i].einit,
+ pval[i].vsync, pval[i].panel, pval[i].scan);
+ }
+
+ hdmi->pvalue = pval;
+ hdmi->nstates = nstates;
+
+ return 0;
+}
+
static int rockchip_hdmiv2_parse_dt(struct hdmi_dev *hdmi_dev)
{
int val = 0;
ret = -ENOMEM;
goto failed1;
}
+
+ hdmi_get_prop_dts(hdmi_dev->hdmi, hdmi_dev->dev->of_node);
mutex_init(&hdmi_dev->ddc_lock);
hdmi_dev->hdmi->dev = &pdev->dev;
hdmi_dev->hdmi->soctype = hdmi_dev->soctype;
1, 3, 0, 0, 0, 0, 3},
{269390000, 269390000, 0, 8, 0, 0, 0,
1, 0, 0, 0, 0, 0, 3},
+ {285000000, 285000000, 0, 8, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 3},
{297000000, 148500000, 0, 8, 0, 0, 0,
1, 0, 1, 0, 0, 0, 3},
{297000000, 297000000, 0, 8, 0, 0, 0,
}
kobject_uevent_env(&dev_drv->dev->kobj, KOBJ_CHANGE, envp);
+ if (dev_drv->cur_screen->width && dev_drv->cur_screen->height) {
+ /* for vr auto dp support */
+ info = rk_fb->fb[dev_drv->fb_index_base];
+ info->var.width = dev_drv->cur_screen->width;
+ info->var.height = dev_drv->cur_screen->height;
+ pr_info("%s:info->var.width=%d, info->var.height=%d\n",
+ __func__, info->var.width, info->var.height);
+ }
+
hdmi_switch_state = 1;
load_screen = true;
dev_drv->hdmi_switch = 0;
char *audioinfo, int len);
int (*getmonspecs)(struct rk_display_device *,
struct fb_monspecs *monspecs);
+ int (*getvrinfo)(struct rk_display_device *, char *);
};
struct rk_display_device {