|
|
@@ -33,13 +33,17 @@
|
|
|
#include <linux/regulator/consumer.h>
|
|
|
#include <linux/io.h>
|
|
|
#include <linux/of.h>
|
|
|
-#include <linux/i2c.h>
|
|
|
+#include <linux/of_address.h>
|
|
|
#include <linux/of_gpio.h>
|
|
|
#include <linux/hdmi.h>
|
|
|
+#include <linux/component.h>
|
|
|
+#include <linux/mfd/syscon.h>
|
|
|
+#include <linux/regmap.h>
|
|
|
|
|
|
#include <drm/exynos_drm.h>
|
|
|
|
|
|
#include "exynos_drm_drv.h"
|
|
|
+#include "exynos_drm_crtc.h"
|
|
|
#include "exynos_mixer.h"
|
|
|
|
|
|
#include <linux/gpio.h>
|
|
|
@@ -48,6 +52,8 @@
|
|
|
#define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev))
|
|
|
#define ctx_from_connector(c) container_of(c, struct hdmi_context, connector)
|
|
|
|
|
|
+#define HOTPLUG_DEBOUNCE_MS 1100
|
|
|
+
|
|
|
/* AVI header and aspect ratio */
|
|
|
#define HDMI_AVI_VERSION 0x02
|
|
|
#define HDMI_AVI_LENGTH 0x0D
|
|
|
@@ -66,6 +72,8 @@ enum hdmi_type {
|
|
|
|
|
|
struct hdmi_driver_data {
|
|
|
unsigned int type;
|
|
|
+ const struct hdmiphy_config *phy_confs;
|
|
|
+ unsigned int phy_conf_count;
|
|
|
unsigned int is_apb_phy:1;
|
|
|
};
|
|
|
|
|
|
@@ -74,7 +82,6 @@ struct hdmi_resources {
|
|
|
struct clk *sclk_hdmi;
|
|
|
struct clk *sclk_pixel;
|
|
|
struct clk *sclk_hdmiphy;
|
|
|
- struct clk *hdmiphy;
|
|
|
struct clk *mout_hdmi;
|
|
|
struct regulator_bulk_data *regul_bulk;
|
|
|
int regul_count;
|
|
|
@@ -185,17 +192,23 @@ struct hdmi_context {
|
|
|
|
|
|
void __iomem *regs;
|
|
|
int irq;
|
|
|
+ struct delayed_work hotplug_work;
|
|
|
|
|
|
struct i2c_adapter *ddc_adpt;
|
|
|
struct i2c_client *hdmiphy_port;
|
|
|
|
|
|
/* current hdmiphy conf regs */
|
|
|
+ struct drm_display_mode current_mode;
|
|
|
struct hdmi_conf_regs mode_conf;
|
|
|
|
|
|
struct hdmi_resources res;
|
|
|
|
|
|
int hpd_gpio;
|
|
|
+ void __iomem *regs_hdmiphy;
|
|
|
+ const struct hdmiphy_config *phy_confs;
|
|
|
+ unsigned int phy_conf_count;
|
|
|
|
|
|
+ struct regmap *pmureg;
|
|
|
enum hdmi_type type;
|
|
|
};
|
|
|
|
|
|
@@ -204,14 +217,6 @@ struct hdmiphy_config {
|
|
|
u8 conf[32];
|
|
|
};
|
|
|
|
|
|
-struct hdmi_driver_data exynos4212_hdmi_driver_data = {
|
|
|
- .type = HDMI_TYPE14,
|
|
|
-};
|
|
|
-
|
|
|
-struct hdmi_driver_data exynos5_hdmi_driver_data = {
|
|
|
- .type = HDMI_TYPE14,
|
|
|
-};
|
|
|
-
|
|
|
/* list of phy config settings */
|
|
|
static const struct hdmiphy_config hdmiphy_v13_configs[] = {
|
|
|
{
|
|
|
@@ -319,18 +324,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
|
|
|
{
|
|
|
.pixel_clock = 71000000,
|
|
|
.conf = {
|
|
|
- 0x01, 0x91, 0x1e, 0x15, 0x40, 0x3c, 0xce, 0x08,
|
|
|
- 0x04, 0x20, 0xb2, 0xd8, 0x45, 0xa0, 0xac, 0x80,
|
|
|
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
+ 0x01, 0xd1, 0x3b, 0x35, 0x40, 0x0c, 0x04, 0x08,
|
|
|
+ 0x85, 0xa0, 0x63, 0xd9, 0x45, 0xa0, 0xac, 0x80,
|
|
|
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
0x54, 0xad, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
.pixel_clock = 73250000,
|
|
|
.conf = {
|
|
|
- 0x01, 0xd1, 0x1f, 0x15, 0x40, 0x18, 0xe9, 0x08,
|
|
|
- 0x02, 0xa0, 0xb7, 0xd8, 0x45, 0xa0, 0xac, 0x80,
|
|
|
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
+ 0x01, 0xd1, 0x3d, 0x35, 0x40, 0x18, 0x02, 0x08,
|
|
|
+ 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
|
|
|
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
0x54, 0xa8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
},
|
|
|
},
|
|
|
@@ -361,15 +366,6 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
|
|
|
0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
},
|
|
|
},
|
|
|
- {
|
|
|
- .pixel_clock = 88750000,
|
|
|
- .conf = {
|
|
|
- 0x01, 0x91, 0x25, 0x17, 0x40, 0x30, 0xfe, 0x08,
|
|
|
- 0x06, 0x20, 0xde, 0xd8, 0x45, 0xa0, 0xac, 0x80,
|
|
|
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
- 0x54, 0x8a, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
- },
|
|
|
- },
|
|
|
{
|
|
|
.pixel_clock = 106500000,
|
|
|
.conf = {
|
|
|
@@ -391,18 +387,18 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
|
|
|
{
|
|
|
.pixel_clock = 115500000,
|
|
|
.conf = {
|
|
|
- 0x01, 0xd1, 0x30, 0x1a, 0x40, 0x40, 0x10, 0x04,
|
|
|
- 0x04, 0xa0, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
|
|
|
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
+ 0x01, 0xd1, 0x30, 0x12, 0x40, 0x40, 0x10, 0x08,
|
|
|
+ 0x80, 0x80, 0x21, 0xd9, 0x45, 0xa0, 0xac, 0x80,
|
|
|
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
0x54, 0xaa, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
|
|
|
},
|
|
|
},
|
|
|
{
|
|
|
.pixel_clock = 119000000,
|
|
|
.conf = {
|
|
|
- 0x01, 0x91, 0x32, 0x14, 0x40, 0x60, 0xd8, 0x08,
|
|
|
- 0x06, 0x20, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
|
|
|
- 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
+ 0x01, 0xd1, 0x32, 0x1a, 0x40, 0x30, 0xd8, 0x08,
|
|
|
+ 0x04, 0xa0, 0x2a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
|
|
|
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
|
|
|
0x54, 0x9d, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
|
|
|
},
|
|
|
},
|
|
|
@@ -426,6 +422,183 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
|
|
|
},
|
|
|
};
|
|
|
|
|
|
+static const struct hdmiphy_config hdmiphy_5420_configs[] = {
|
|
|
+ {
|
|
|
+ .pixel_clock = 25200000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8,
|
|
|
+ 0x82, 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x06, 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 27000000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0,
|
|
|
+ 0x98, 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x06, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 27027000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8,
|
|
|
+ 0x43, 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x26, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 36000000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8,
|
|
|
+ 0x02, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 40000000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8,
|
|
|
+ 0x87, 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 65000000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8,
|
|
|
+ 0x82, 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 71000000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8,
|
|
|
+ 0x85, 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 73250000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x78, 0x8D, 0xC8,
|
|
|
+ 0x81, 0xE8, 0xB7, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xA8, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 74176000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8,
|
|
|
+ 0x81, 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x56, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 74250000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08,
|
|
|
+ 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
|
|
|
+ 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 83500000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8,
|
|
|
+ 0x85, 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 88750000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x25, 0x11, 0x40, 0x18, 0xFF, 0xC8,
|
|
|
+ 0x83, 0xE8, 0xDE, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x45, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 106500000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8,
|
|
|
+ 0x84, 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 108000000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8,
|
|
|
+ 0x82, 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 115500000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x30, 0x14, 0x40, 0x0C, 0x03, 0xC8,
|
|
|
+ 0x88, 0xE8, 0x21, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x6A, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 146250000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8,
|
|
|
+ 0x83, 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x08, 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66,
|
|
|
+ 0x54, 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ .pixel_clock = 148500000,
|
|
|
+ .conf = {
|
|
|
+ 0x01, 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08,
|
|
|
+ 0x81, 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80,
|
|
|
+ 0x26, 0x80, 0x09, 0x84, 0x05, 0x22, 0x24, 0x66,
|
|
|
+ 0x54, 0x4B, 0x25, 0x03, 0x00, 0x80, 0x01, 0x80,
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+static struct hdmi_driver_data exynos5420_hdmi_driver_data = {
|
|
|
+ .type = HDMI_TYPE14,
|
|
|
+ .phy_confs = hdmiphy_5420_configs,
|
|
|
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_5420_configs),
|
|
|
+ .is_apb_phy = 1,
|
|
|
+};
|
|
|
+
|
|
|
+static struct hdmi_driver_data exynos4212_hdmi_driver_data = {
|
|
|
+ .type = HDMI_TYPE14,
|
|
|
+ .phy_confs = hdmiphy_v14_configs,
|
|
|
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_v14_configs),
|
|
|
+ .is_apb_phy = 0,
|
|
|
+};
|
|
|
+
|
|
|
+static struct hdmi_driver_data exynos5_hdmi_driver_data = {
|
|
|
+ .type = HDMI_TYPE14,
|
|
|
+ .phy_confs = hdmiphy_v13_configs,
|
|
|
+ .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
|
|
|
+ .is_apb_phy = 0,
|
|
|
+};
|
|
|
+
|
|
|
static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
|
|
|
{
|
|
|
return readl(hdata->regs + reg_id);
|
|
|
@@ -445,6 +618,48 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
|
|
|
writel(value, hdata->regs + reg_id);
|
|
|
}
|
|
|
|
|
|
+static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
|
|
|
+ u32 reg_offset, u8 value)
|
|
|
+{
|
|
|
+ if (hdata->hdmiphy_port) {
|
|
|
+ u8 buffer[2];
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ buffer[0] = reg_offset;
|
|
|
+ buffer[1] = value;
|
|
|
+
|
|
|
+ ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
|
|
|
+ if (ret == 2)
|
|
|
+ return 0;
|
|
|
+ return ret;
|
|
|
+ } else {
|
|
|
+ writeb(value, hdata->regs_hdmiphy + (reg_offset<<2));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
|
|
|
+ u32 reg_offset, const u8 *buf, u32 len)
|
|
|
+{
|
|
|
+ if ((reg_offset + len) > 32)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (hdata->hdmiphy_port) {
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
|
|
|
+ if (ret == len)
|
|
|
+ return 0;
|
|
|
+ return ret;
|
|
|
+ } else {
|
|
|
+ int i;
|
|
|
+ for (i = 0; i < len; i++)
|
|
|
+ writeb(buf[i], hdata->regs_hdmiphy +
|
|
|
+ ((reg_offset + i)<<2));
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
|
|
|
{
|
|
|
#define DUMPREG(reg_id) \
|
|
|
@@ -809,6 +1024,8 @@ static enum drm_connector_status hdmi_detect(struct drm_connector *connector,
|
|
|
{
|
|
|
struct hdmi_context *hdata = ctx_from_connector(connector);
|
|
|
|
|
|
+ hdata->hpd = gpio_get_value(hdata->hpd_gpio);
|
|
|
+
|
|
|
return hdata->hpd ? connector_status_connected :
|
|
|
connector_status_disconnected;
|
|
|
}
|
|
|
@@ -848,20 +1065,10 @@ static int hdmi_get_modes(struct drm_connector *connector)
|
|
|
|
|
|
static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
|
|
|
{
|
|
|
- const struct hdmiphy_config *confs;
|
|
|
- int count, i;
|
|
|
-
|
|
|
- if (hdata->type == HDMI_TYPE13) {
|
|
|
- confs = hdmiphy_v13_configs;
|
|
|
- count = ARRAY_SIZE(hdmiphy_v13_configs);
|
|
|
- } else if (hdata->type == HDMI_TYPE14) {
|
|
|
- confs = hdmiphy_v14_configs;
|
|
|
- count = ARRAY_SIZE(hdmiphy_v14_configs);
|
|
|
- } else
|
|
|
- return -EINVAL;
|
|
|
+ int i;
|
|
|
|
|
|
- for (i = 0; i < count; i++)
|
|
|
- if (confs[i].pixel_clock == pixel_clock)
|
|
|
+ for (i = 0; i < hdata->phy_conf_count; i++)
|
|
|
+ if (hdata->phy_confs[i].pixel_clock == pixel_clock)
|
|
|
return i;
|
|
|
|
|
|
DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
|
|
|
@@ -928,16 +1135,6 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int hdmi_initialize(struct exynos_drm_display *display,
|
|
|
- struct drm_device *drm_dev)
|
|
|
-{
|
|
|
- struct hdmi_context *hdata = display->ctx;
|
|
|
-
|
|
|
- hdata->drm_dev = drm_dev;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static void hdmi_mode_fixup(struct exynos_drm_display *display,
|
|
|
struct drm_connector *connector,
|
|
|
const struct drm_display_mode *mode,
|
|
|
@@ -1136,20 +1333,15 @@ static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
|
|
|
HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
|
|
|
}
|
|
|
|
|
|
-static void hdmi_conf_reset(struct hdmi_context *hdata)
|
|
|
+static void hdmi_start(struct hdmi_context *hdata, bool start)
|
|
|
{
|
|
|
- u32 reg;
|
|
|
+ u32 val = start ? HDMI_TG_EN : 0;
|
|
|
|
|
|
- if (hdata->type == HDMI_TYPE13)
|
|
|
- reg = HDMI_V13_CORE_RSTOUT;
|
|
|
- else
|
|
|
- reg = HDMI_CORE_RSTOUT;
|
|
|
+ if (hdata->current_mode.flags & DRM_MODE_FLAG_INTERLACE)
|
|
|
+ val |= HDMI_FIELD_EN;
|
|
|
|
|
|
- /* resetting HDMI core */
|
|
|
- hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT);
|
|
|
- usleep_range(10000, 12000);
|
|
|
- hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
|
|
|
- usleep_range(10000, 12000);
|
|
|
+ hdmi_reg_writemask(hdata, HDMI_CON_0, val, HDMI_EN);
|
|
|
+ hdmi_reg_writemask(hdata, HDMI_TG_CMD, val, HDMI_TG_EN | HDMI_FIELD_EN);
|
|
|
}
|
|
|
|
|
|
static void hdmi_conf_init(struct hdmi_context *hdata)
|
|
|
@@ -1163,6 +1355,8 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
|
|
|
/* choose HDMI mode */
|
|
|
hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
|
|
|
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
|
|
|
+ /* Apply Video preable and Guard band in HDMI mode only */
|
|
|
+ hdmi_reg_writeb(hdata, HDMI_CON_2, 0);
|
|
|
/* disable bluescreen */
|
|
|
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
|
|
|
|
|
|
@@ -1286,12 +1480,7 @@ static void hdmi_v13_mode_apply(struct hdmi_context *hdata)
|
|
|
clk_prepare_enable(hdata->res.sclk_hdmi);
|
|
|
|
|
|
/* enable HDMI and timing generator */
|
|
|
- hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
|
|
|
- if (core->int_pro_mode[0])
|
|
|
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
|
|
|
- HDMI_FIELD_EN);
|
|
|
- else
|
|
|
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
|
|
|
+ hdmi_start(hdata, true);
|
|
|
}
|
|
|
|
|
|
static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
|
|
|
@@ -1453,12 +1642,7 @@ static void hdmi_v14_mode_apply(struct hdmi_context *hdata)
|
|
|
clk_prepare_enable(hdata->res.sclk_hdmi);
|
|
|
|
|
|
/* enable HDMI and timing generator */
|
|
|
- hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
|
|
|
- if (core->int_pro_mode[0])
|
|
|
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
|
|
|
- HDMI_FIELD_EN);
|
|
|
- else
|
|
|
- hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
|
|
|
+ hdmi_start(hdata, true);
|
|
|
}
|
|
|
|
|
|
static void hdmi_mode_apply(struct hdmi_context *hdata)
|
|
|
@@ -1499,32 +1683,51 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
|
|
|
|
|
|
static void hdmiphy_poweron(struct hdmi_context *hdata)
|
|
|
{
|
|
|
- if (hdata->type == HDMI_TYPE14)
|
|
|
- hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, 0,
|
|
|
- HDMI_PHY_POWER_OFF_EN);
|
|
|
+ if (hdata->type != HDMI_TYPE14)
|
|
|
+ return;
|
|
|
+
|
|
|
+ DRM_DEBUG_KMS("\n");
|
|
|
+
|
|
|
+ /* For PHY Mode Setting */
|
|
|
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
|
|
|
+ HDMI_PHY_ENABLE_MODE_SET);
|
|
|
+ /* Phy Power On */
|
|
|
+ hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
|
|
|
+ HDMI_PHY_POWER_ON);
|
|
|
+ /* For PHY Mode Setting */
|
|
|
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
|
|
|
+ HDMI_PHY_DISABLE_MODE_SET);
|
|
|
+ /* PHY SW Reset */
|
|
|
+ hdmiphy_conf_reset(hdata);
|
|
|
}
|
|
|
|
|
|
static void hdmiphy_poweroff(struct hdmi_context *hdata)
|
|
|
{
|
|
|
- if (hdata->type == HDMI_TYPE14)
|
|
|
- hdmi_reg_writemask(hdata, HDMI_PHY_CON_0, ~0,
|
|
|
- HDMI_PHY_POWER_OFF_EN);
|
|
|
+ if (hdata->type != HDMI_TYPE14)
|
|
|
+ return;
|
|
|
+
|
|
|
+ DRM_DEBUG_KMS("\n");
|
|
|
+
|
|
|
+ /* PHY SW Reset */
|
|
|
+ hdmiphy_conf_reset(hdata);
|
|
|
+ /* For PHY Mode Setting */
|
|
|
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
|
|
|
+ HDMI_PHY_ENABLE_MODE_SET);
|
|
|
+
|
|
|
+ /* PHY Power Off */
|
|
|
+ hdmiphy_reg_writeb(hdata, HDMIPHY_POWER,
|
|
|
+ HDMI_PHY_POWER_OFF);
|
|
|
+
|
|
|
+ /* For PHY Mode Setting */
|
|
|
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
|
|
|
+ HDMI_PHY_DISABLE_MODE_SET);
|
|
|
}
|
|
|
|
|
|
static void hdmiphy_conf_apply(struct hdmi_context *hdata)
|
|
|
{
|
|
|
- const u8 *hdmiphy_data;
|
|
|
- u8 buffer[32];
|
|
|
- u8 operation[2];
|
|
|
- u8 read_buffer[32] = {0, };
|
|
|
int ret;
|
|
|
int i;
|
|
|
|
|
|
- if (!hdata->hdmiphy_port) {
|
|
|
- DRM_ERROR("hdmiphy is not attached\n");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
/* pixel clock */
|
|
|
i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
|
|
|
if (i < 0) {
|
|
|
@@ -1532,39 +1735,21 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (hdata->type == HDMI_TYPE13)
|
|
|
- hdmiphy_data = hdmiphy_v13_configs[i].conf;
|
|
|
- else
|
|
|
- hdmiphy_data = hdmiphy_v14_configs[i].conf;
|
|
|
-
|
|
|
- memcpy(buffer, hdmiphy_data, 32);
|
|
|
- ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
|
|
|
- if (ret != 32) {
|
|
|
- DRM_ERROR("failed to configure HDMIPHY via I2C\n");
|
|
|
+ ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32);
|
|
|
+ if (ret) {
|
|
|
+ DRM_ERROR("failed to configure hdmiphy\n");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
usleep_range(10000, 12000);
|
|
|
|
|
|
- /* operation mode */
|
|
|
- operation[0] = 0x1f;
|
|
|
- operation[1] = 0x80;
|
|
|
-
|
|
|
- ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
|
|
|
- if (ret != 2) {
|
|
|
+ ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
|
|
|
+ HDMI_PHY_DISABLE_MODE_SET);
|
|
|
+ if (ret) {
|
|
|
DRM_ERROR("failed to enable hdmiphy\n");
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
|
|
|
- if (ret < 0) {
|
|
|
- DRM_ERROR("failed to read hdmiphy config\n");
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < ret; i++)
|
|
|
- DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
|
|
|
- "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
|
|
|
}
|
|
|
|
|
|
static void hdmi_conf_apply(struct hdmi_context *hdata)
|
|
|
@@ -1573,7 +1758,7 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
|
|
|
hdmiphy_conf_apply(hdata);
|
|
|
|
|
|
mutex_lock(&hdata->hdmi_mutex);
|
|
|
- hdmi_conf_reset(hdata);
|
|
|
+ hdmi_start(hdata, false);
|
|
|
hdmi_conf_init(hdata);
|
|
|
mutex_unlock(&hdata->hdmi_mutex);
|
|
|
|
|
|
@@ -1814,6 +1999,9 @@ static void hdmi_mode_set(struct exynos_drm_display *display,
|
|
|
m->vrefresh, (m->flags & DRM_MODE_FLAG_INTERLACE) ?
|
|
|
"INTERLACED" : "PROGERESSIVE");
|
|
|
|
|
|
+ /* preserve mode information for later use. */
|
|
|
+ drm_mode_copy(&hdata->current_mode, mode);
|
|
|
+
|
|
|
if (hdata->type == HDMI_TYPE13)
|
|
|
hdmi_v13_mode_set(hdata, mode);
|
|
|
else
|
|
|
@@ -1854,7 +2042,10 @@ static void hdmi_poweron(struct exynos_drm_display *display)
|
|
|
if (regulator_bulk_enable(res->regul_count, res->regul_bulk))
|
|
|
DRM_DEBUG_KMS("failed to enable regulator bulk\n");
|
|
|
|
|
|
- clk_prepare_enable(res->hdmiphy);
|
|
|
+ /* set pmu hdmiphy control bit to enable hdmiphy */
|
|
|
+ regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
|
|
|
+ PMU_HDMI_PHY_ENABLE_BIT, 1);
|
|
|
+
|
|
|
clk_prepare_enable(res->hdmi);
|
|
|
clk_prepare_enable(res->sclk_hdmi);
|
|
|
|
|
|
@@ -1872,16 +2063,20 @@ static void hdmi_poweroff(struct exynos_drm_display *display)
|
|
|
goto out;
|
|
|
mutex_unlock(&hdata->hdmi_mutex);
|
|
|
|
|
|
- /*
|
|
|
- * The TV power domain needs any condition of hdmiphy to turn off and
|
|
|
- * its reset state seems to meet the condition.
|
|
|
- */
|
|
|
- hdmiphy_conf_reset(hdata);
|
|
|
+ /* HDMI System Disable */
|
|
|
+ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_EN);
|
|
|
+
|
|
|
hdmiphy_poweroff(hdata);
|
|
|
|
|
|
+ cancel_delayed_work(&hdata->hotplug_work);
|
|
|
+
|
|
|
clk_disable_unprepare(res->sclk_hdmi);
|
|
|
clk_disable_unprepare(res->hdmi);
|
|
|
- clk_disable_unprepare(res->hdmiphy);
|
|
|
+
|
|
|
+ /* reset pmu hdmiphy control bit to disable hdmiphy */
|
|
|
+ regmap_update_bits(hdata->pmureg, PMU_HDMI_PHY_CONTROL,
|
|
|
+ PMU_HDMI_PHY_ENABLE_BIT, 0);
|
|
|
+
|
|
|
regulator_bulk_disable(res->regul_count, res->regul_bulk);
|
|
|
|
|
|
pm_runtime_put_sync(hdata->dev);
|
|
|
@@ -1913,7 +2108,6 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode)
|
|
|
}
|
|
|
|
|
|
static struct exynos_drm_display_ops hdmi_display_ops = {
|
|
|
- .initialize = hdmi_initialize,
|
|
|
.create_connector = hdmi_create_connector,
|
|
|
.mode_fixup = hdmi_mode_fixup,
|
|
|
.mode_set = hdmi_mode_set,
|
|
|
@@ -1926,9 +2120,11 @@ static struct exynos_drm_display hdmi_display = {
|
|
|
.ops = &hdmi_display_ops,
|
|
|
};
|
|
|
|
|
|
-static irqreturn_t hdmi_irq_thread(int irq, void *arg)
|
|
|
+static void hdmi_hotplug_work_func(struct work_struct *work)
|
|
|
{
|
|
|
- struct hdmi_context *hdata = arg;
|
|
|
+ struct hdmi_context *hdata;
|
|
|
+
|
|
|
+ hdata = container_of(work, struct hdmi_context, hotplug_work.work);
|
|
|
|
|
|
mutex_lock(&hdata->hdmi_mutex);
|
|
|
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
|
|
|
@@ -1936,6 +2132,14 @@ static irqreturn_t hdmi_irq_thread(int irq, void *arg)
|
|
|
|
|
|
if (hdata->drm_dev)
|
|
|
drm_helper_hpd_irq_event(hdata->drm_dev);
|
|
|
+}
|
|
|
+
|
|
|
+static irqreturn_t hdmi_irq_thread(int irq, void *arg)
|
|
|
+{
|
|
|
+ struct hdmi_context *hdata = arg;
|
|
|
+
|
|
|
+ mod_delayed_work(system_wq, &hdata->hotplug_work,
|
|
|
+ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
@@ -1954,37 +2158,35 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
|
|
|
|
|
|
DRM_DEBUG_KMS("HDMI resource init\n");
|
|
|
|
|
|
- memset(res, 0, sizeof(*res));
|
|
|
-
|
|
|
/* get clocks, power */
|
|
|
res->hdmi = devm_clk_get(dev, "hdmi");
|
|
|
if (IS_ERR(res->hdmi)) {
|
|
|
DRM_ERROR("failed to get clock 'hdmi'\n");
|
|
|
+ ret = PTR_ERR(res->hdmi);
|
|
|
goto fail;
|
|
|
}
|
|
|
res->sclk_hdmi = devm_clk_get(dev, "sclk_hdmi");
|
|
|
if (IS_ERR(res->sclk_hdmi)) {
|
|
|
DRM_ERROR("failed to get clock 'sclk_hdmi'\n");
|
|
|
+ ret = PTR_ERR(res->sclk_hdmi);
|
|
|
goto fail;
|
|
|
}
|
|
|
res->sclk_pixel = devm_clk_get(dev, "sclk_pixel");
|
|
|
if (IS_ERR(res->sclk_pixel)) {
|
|
|
DRM_ERROR("failed to get clock 'sclk_pixel'\n");
|
|
|
+ ret = PTR_ERR(res->sclk_pixel);
|
|
|
goto fail;
|
|
|
}
|
|
|
res->sclk_hdmiphy = devm_clk_get(dev, "sclk_hdmiphy");
|
|
|
if (IS_ERR(res->sclk_hdmiphy)) {
|
|
|
DRM_ERROR("failed to get clock 'sclk_hdmiphy'\n");
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- res->hdmiphy = devm_clk_get(dev, "hdmiphy");
|
|
|
- if (IS_ERR(res->hdmiphy)) {
|
|
|
- DRM_ERROR("failed to get clock 'hdmiphy'\n");
|
|
|
+ ret = PTR_ERR(res->sclk_hdmiphy);
|
|
|
goto fail;
|
|
|
}
|
|
|
res->mout_hdmi = devm_clk_get(dev, "mout_hdmi");
|
|
|
if (IS_ERR(res->mout_hdmi)) {
|
|
|
DRM_ERROR("failed to get clock 'mout_hdmi'\n");
|
|
|
+ ret = PTR_ERR(res->mout_hdmi);
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
@@ -1992,8 +2194,10 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
|
|
|
|
|
|
res->regul_bulk = devm_kzalloc(dev, ARRAY_SIZE(supply) *
|
|
|
sizeof(res->regul_bulk[0]), GFP_KERNEL);
|
|
|
- if (!res->regul_bulk)
|
|
|
+ if (!res->regul_bulk) {
|
|
|
+ ret = -ENOMEM;
|
|
|
goto fail;
|
|
|
+ }
|
|
|
for (i = 0; i < ARRAY_SIZE(supply); ++i) {
|
|
|
res->regul_bulk[i].supply = supply[i];
|
|
|
res->regul_bulk[i].consumer = NULL;
|
|
|
@@ -2001,14 +2205,14 @@ static int hdmi_resources_init(struct hdmi_context *hdata)
|
|
|
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(supply), res->regul_bulk);
|
|
|
if (ret) {
|
|
|
DRM_ERROR("failed to get regulators\n");
|
|
|
- goto fail;
|
|
|
+ return ret;
|
|
|
}
|
|
|
res->regul_count = ARRAY_SIZE(supply);
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
fail:
|
|
|
DRM_ERROR("HDMI resource init - failed\n");
|
|
|
- return -ENODEV;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata
|
|
|
@@ -2042,43 +2246,106 @@ static struct of_device_id hdmi_match_types[] = {
|
|
|
}, {
|
|
|
.compatible = "samsung,exynos4212-hdmi",
|
|
|
.data = &exynos4212_hdmi_driver_data,
|
|
|
+ }, {
|
|
|
+ .compatible = "samsung,exynos5420-hdmi",
|
|
|
+ .data = &exynos5420_hdmi_driver_data,
|
|
|
}, {
|
|
|
/* end node */
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+static int hdmi_bind(struct device *dev, struct device *master, void *data)
|
|
|
+{
|
|
|
+ struct drm_device *drm_dev = data;
|
|
|
+ struct hdmi_context *hdata;
|
|
|
+
|
|
|
+ hdata = hdmi_display.ctx;
|
|
|
+ hdata->drm_dev = drm_dev;
|
|
|
+
|
|
|
+ return exynos_drm_create_enc_conn(drm_dev, &hdmi_display);
|
|
|
+}
|
|
|
+
|
|
|
+static void hdmi_unbind(struct device *dev, struct device *master, void *data)
|
|
|
+{
|
|
|
+ struct exynos_drm_display *display = get_hdmi_display(dev);
|
|
|
+ struct drm_encoder *encoder = display->encoder;
|
|
|
+ struct hdmi_context *hdata = display->ctx;
|
|
|
+
|
|
|
+ encoder->funcs->destroy(encoder);
|
|
|
+ drm_connector_cleanup(&hdata->connector);
|
|
|
+}
|
|
|
+
|
|
|
+static const struct component_ops hdmi_component_ops = {
|
|
|
+ .bind = hdmi_bind,
|
|
|
+ .unbind = hdmi_unbind,
|
|
|
+};
|
|
|
+
|
|
|
+static struct device_node *hdmi_legacy_ddc_dt_binding(struct device *dev)
|
|
|
+{
|
|
|
+ const char *compatible_str = "samsung,exynos4210-hdmiddc";
|
|
|
+ struct device_node *np;
|
|
|
+
|
|
|
+ np = of_find_compatible_node(NULL, NULL, compatible_str);
|
|
|
+ if (np)
|
|
|
+ return of_get_next_parent(np);
|
|
|
+
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static struct device_node *hdmi_legacy_phy_dt_binding(struct device *dev)
|
|
|
+{
|
|
|
+ const char *compatible_str = "samsung,exynos4212-hdmiphy";
|
|
|
+
|
|
|
+ return of_find_compatible_node(NULL, NULL, compatible_str);
|
|
|
+}
|
|
|
+
|
|
|
static int hdmi_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
+ struct device_node *ddc_node, *phy_node;
|
|
|
+ struct s5p_hdmi_platform_data *pdata;
|
|
|
+ struct hdmi_driver_data *drv_data;
|
|
|
+ const struct of_device_id *match;
|
|
|
struct device *dev = &pdev->dev;
|
|
|
struct hdmi_context *hdata;
|
|
|
- struct s5p_hdmi_platform_data *pdata;
|
|
|
struct resource *res;
|
|
|
- const struct of_device_id *match;
|
|
|
- struct device_node *ddc_node, *phy_node;
|
|
|
- struct hdmi_driver_data *drv_data;
|
|
|
int ret;
|
|
|
|
|
|
- if (!dev->of_node)
|
|
|
- return -ENODEV;
|
|
|
+ ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR,
|
|
|
+ hdmi_display.type);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ if (!dev->of_node) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto err_del_component;
|
|
|
+ }
|
|
|
|
|
|
pdata = drm_hdmi_dt_parse_pdata(dev);
|
|
|
- if (!pdata)
|
|
|
- return -EINVAL;
|
|
|
+ if (!pdata) {
|
|
|
+ ret = -EINVAL;
|
|
|
+ goto err_del_component;
|
|
|
+ }
|
|
|
|
|
|
hdata = devm_kzalloc(dev, sizeof(struct hdmi_context), GFP_KERNEL);
|
|
|
- if (!hdata)
|
|
|
- return -ENOMEM;
|
|
|
+ if (!hdata) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_del_component;
|
|
|
+ }
|
|
|
|
|
|
mutex_init(&hdata->hdmi_mutex);
|
|
|
|
|
|
platform_set_drvdata(pdev, &hdmi_display);
|
|
|
|
|
|
match = of_match_node(hdmi_match_types, dev->of_node);
|
|
|
- if (!match)
|
|
|
- return -ENODEV;
|
|
|
+ if (!match) {
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto err_del_component;
|
|
|
+ }
|
|
|
|
|
|
drv_data = (struct hdmi_driver_data *)match->data;
|
|
|
hdata->type = drv_data->type;
|
|
|
+ hdata->phy_confs = drv_data->phy_confs;
|
|
|
+ hdata->phy_conf_count = drv_data->phy_conf_count;
|
|
|
|
|
|
hdata->hpd_gpio = pdata->hpd_gpio;
|
|
|
hdata->dev = dev;
|
|
|
@@ -2086,35 +2353,44 @@ static int hdmi_probe(struct platform_device *pdev)
|
|
|
ret = hdmi_resources_init(hdata);
|
|
|
if (ret) {
|
|
|
DRM_ERROR("hdmi_resources_init failed\n");
|
|
|
- return -EINVAL;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
hdata->regs = devm_ioremap_resource(dev, res);
|
|
|
- if (IS_ERR(hdata->regs))
|
|
|
- return PTR_ERR(hdata->regs);
|
|
|
+ if (IS_ERR(hdata->regs)) {
|
|
|
+ ret = PTR_ERR(hdata->regs);
|
|
|
+ goto err_del_component;
|
|
|
+ }
|
|
|
|
|
|
ret = devm_gpio_request(dev, hdata->hpd_gpio, "HPD");
|
|
|
if (ret) {
|
|
|
DRM_ERROR("failed to request HPD gpio\n");
|
|
|
- return ret;
|
|
|
+ goto err_del_component;
|
|
|
}
|
|
|
|
|
|
+ ddc_node = hdmi_legacy_ddc_dt_binding(dev);
|
|
|
+ if (ddc_node)
|
|
|
+ goto out_get_ddc_adpt;
|
|
|
+
|
|
|
/* DDC i2c driver */
|
|
|
ddc_node = of_parse_phandle(dev->of_node, "ddc", 0);
|
|
|
if (!ddc_node) {
|
|
|
DRM_ERROR("Failed to find ddc node in device tree\n");
|
|
|
- return -ENODEV;
|
|
|
+ ret = -ENODEV;
|
|
|
+ goto err_del_component;
|
|
|
}
|
|
|
+
|
|
|
+out_get_ddc_adpt:
|
|
|
hdata->ddc_adpt = of_find_i2c_adapter_by_node(ddc_node);
|
|
|
if (!hdata->ddc_adpt) {
|
|
|
DRM_ERROR("Failed to get ddc i2c adapter by node\n");
|
|
|
- return -ENODEV;
|
|
|
+ return -EPROBE_DEFER;
|
|
|
}
|
|
|
|
|
|
- /* Not support APB PHY yet. */
|
|
|
- if (drv_data->is_apb_phy)
|
|
|
- return -EPERM;
|
|
|
+ phy_node = hdmi_legacy_phy_dt_binding(dev);
|
|
|
+ if (phy_node)
|
|
|
+ goto out_get_phy_port;
|
|
|
|
|
|
/* hdmiphy i2c driver */
|
|
|
phy_node = of_parse_phandle(dev->of_node, "phy", 0);
|
|
|
@@ -2123,11 +2399,22 @@ static int hdmi_probe(struct platform_device *pdev)
|
|
|
ret = -ENODEV;
|
|
|
goto err_ddc;
|
|
|
}
|
|
|
- hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
|
|
|
- if (!hdata->hdmiphy_port) {
|
|
|
- DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
|
|
|
- ret = -ENODEV;
|
|
|
- goto err_ddc;
|
|
|
+
|
|
|
+out_get_phy_port:
|
|
|
+ if (drv_data->is_apb_phy) {
|
|
|
+ hdata->regs_hdmiphy = of_iomap(phy_node, 0);
|
|
|
+ if (!hdata->regs_hdmiphy) {
|
|
|
+ DRM_ERROR("failed to ioremap hdmi phy\n");
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_ddc;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
|
|
|
+ if (!hdata->hdmiphy_port) {
|
|
|
+ DRM_ERROR("Failed to get hdmi phy i2c client\n");
|
|
|
+ ret = -EPROBE_DEFER;
|
|
|
+ goto err_ddc;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
hdata->irq = gpio_to_irq(hdata->hpd_gpio);
|
|
|
@@ -2139,6 +2426,8 @@ static int hdmi_probe(struct platform_device *pdev)
|
|
|
|
|
|
hdata->hpd = gpio_get_value(hdata->hpd_gpio);
|
|
|
|
|
|
+ INIT_DELAYED_WORK(&hdata->hotplug_work, hdmi_hotplug_work_func);
|
|
|
+
|
|
|
ret = devm_request_threaded_irq(dev, hdata->irq, NULL,
|
|
|
hdmi_irq_thread, IRQF_TRIGGER_RISING |
|
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
|
@@ -2148,30 +2437,51 @@ static int hdmi_probe(struct platform_device *pdev)
|
|
|
goto err_hdmiphy;
|
|
|
}
|
|
|
|
|
|
- pm_runtime_enable(dev);
|
|
|
+ hdata->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
|
|
|
+ "samsung,syscon-phandle");
|
|
|
+ if (IS_ERR(hdata->pmureg)) {
|
|
|
+ DRM_ERROR("syscon regmap lookup failed.\n");
|
|
|
+ ret = -EPROBE_DEFER;
|
|
|
+ goto err_hdmiphy;
|
|
|
+ }
|
|
|
|
|
|
+ pm_runtime_enable(dev);
|
|
|
hdmi_display.ctx = hdata;
|
|
|
- exynos_drm_display_register(&hdmi_display);
|
|
|
|
|
|
- return 0;
|
|
|
+ ret = component_add(&pdev->dev, &hdmi_component_ops);
|
|
|
+ if (ret)
|
|
|
+ goto err_disable_pm_runtime;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+
|
|
|
+err_disable_pm_runtime:
|
|
|
+ pm_runtime_disable(dev);
|
|
|
|
|
|
err_hdmiphy:
|
|
|
- put_device(&hdata->hdmiphy_port->dev);
|
|
|
+ if (hdata->hdmiphy_port)
|
|
|
+ put_device(&hdata->hdmiphy_port->dev);
|
|
|
err_ddc:
|
|
|
put_device(&hdata->ddc_adpt->dev);
|
|
|
+
|
|
|
+err_del_component:
|
|
|
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
static int hdmi_remove(struct platform_device *pdev)
|
|
|
{
|
|
|
- struct device *dev = &pdev->dev;
|
|
|
- struct exynos_drm_display *display = get_hdmi_display(dev);
|
|
|
- struct hdmi_context *hdata = display->ctx;
|
|
|
+ struct hdmi_context *hdata = hdmi_display.ctx;
|
|
|
+
|
|
|
+ cancel_delayed_work_sync(&hdata->hotplug_work);
|
|
|
|
|
|
put_device(&hdata->hdmiphy_port->dev);
|
|
|
put_device(&hdata->ddc_adpt->dev);
|
|
|
+
|
|
|
pm_runtime_disable(&pdev->dev);
|
|
|
+ component_del(&pdev->dev, &hdmi_component_ops);
|
|
|
|
|
|
+ exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);
|
|
|
return 0;
|
|
|
}
|
|
|
|