diff options
Diffstat (limited to 'drivers/gpu/drm/exynos/exynos_hdmi.c')
| -rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 1073 | 
1 files changed, 745 insertions, 328 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index a0e10aeb0e6..aa259b0a873 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -33,56 +33,55 @@  #include <linux/regulator/consumer.h>  #include <linux/io.h>  #include <linux/of.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_hdmi.h" - -#include "exynos_hdmi.h" +#include "exynos_drm_crtc.h" +#include "exynos_mixer.h"  #include <linux/gpio.h>  #include <media/s5p_hdmi.h> -#define MAX_WIDTH		1920 -#define MAX_HEIGHT		1080 -#define get_hdmi_context(dev)	platform_get_drvdata(to_platform_device(dev)) +#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 -#define AVI_PIC_ASPECT_RATIO_16_9	(2 << 4) -#define AVI_SAME_AS_PIC_ASPECT_RATIO	8  /* AUI header info */  #define HDMI_AUI_VERSION	0x01  #define HDMI_AUI_LENGTH	0x0A - -/* HDMI infoframe to configure HDMI out packet header, AUI and AVI */ -enum HDMI_PACKET_TYPE { -	/* refer to Table 5-8 Packet Type in HDMI specification v1.4a */ -	/* InfoFrame packet type */ -	HDMI_PACKET_TYPE_INFOFRAME = 0x80, -	/* Vendor-Specific InfoFrame */ -	HDMI_PACKET_TYPE_VSI = HDMI_PACKET_TYPE_INFOFRAME + 1, -	/* Auxiliary Video information InfoFrame */ -	HDMI_PACKET_TYPE_AVI = HDMI_PACKET_TYPE_INFOFRAME + 2, -	/* Audio information InfoFrame */ -	HDMI_PACKET_TYPE_AUI = HDMI_PACKET_TYPE_INFOFRAME + 4 -}; +#define AVI_SAME_AS_PIC_ASPECT_RATIO 0x8 +#define AVI_4_3_CENTER_RATIO	0x9 +#define AVI_16_9_CENTER_RATIO	0xa  enum hdmi_type {  	HDMI_TYPE13,  	HDMI_TYPE14,  }; +struct hdmi_driver_data { +	unsigned int type; +	const struct hdmiphy_config *phy_confs; +	unsigned int phy_conf_count; +	unsigned int is_apb_phy:1; +}; +  struct hdmi_resources {  	struct clk			*hdmi;  	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; @@ -174,6 +173,7 @@ struct hdmi_v14_conf {  struct hdmi_conf_regs {  	int pixel_clock;  	int cea_video_id; +	enum hdmi_picture_aspect aspect_ratio;  	union {  		struct hdmi_v13_conf v13_conf;  		struct hdmi_v14_conf v14_conf; @@ -183,25 +183,32 @@ struct hdmi_conf_regs {  struct hdmi_context {  	struct device			*dev;  	struct drm_device		*drm_dev; +	struct drm_connector		connector; +	struct drm_encoder		*encoder;  	bool				hpd;  	bool				powered;  	bool				dvi_mode;  	struct mutex			hdmi_mutex;  	void __iomem			*regs; -	void				*parent_ctx;  	int				irq; +	struct delayed_work		hotplug_work; -	struct i2c_client		*ddc_port; +	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;  }; @@ -315,6 +322,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {  		},  	},  	{ +		.pixel_clock = 71000000, +		.conf = { +			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, 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, +		}, +	}, +	{  		.pixel_clock = 74176000,  		.conf = {  			0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08, @@ -360,6 +385,24 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {  		},  	},  	{ +		.pixel_clock = 115500000, +		.conf = { +			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, 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, +		}, +	}, +	{  		.pixel_clock = 146250000,  		.conf = {  			0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08, @@ -379,10 +422,181 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {  	},  }; -struct hdmi_infoframe { -	enum HDMI_PACKET_TYPE type; -	u8 ver; -	u8 len; +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) @@ -404,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) \ @@ -682,11 +938,10 @@ static u8 hdmi_chksum(struct hdmi_context *hdata,  }  static void hdmi_reg_infoframe(struct hdmi_context *hdata, -			struct hdmi_infoframe *infoframe) +			union hdmi_infoframe *infoframe)  {  	u32 hdr_sum;  	u8 chksum; -	u32 aspect_ratio;  	u32 mod;  	u32 vic; @@ -700,40 +955,62 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,  		return;  	} -	switch (infoframe->type) { -	case HDMI_PACKET_TYPE_AVI: +	switch (infoframe->any.type) { +	case HDMI_INFOFRAME_TYPE_AVI:  		hdmi_reg_writeb(hdata, HDMI_AVI_CON, HDMI_AVI_CON_EVERY_VSYNC); -		hdmi_reg_writeb(hdata, HDMI_AVI_HEADER0, infoframe->type); -		hdmi_reg_writeb(hdata, HDMI_AVI_HEADER1, infoframe->ver); -		hdmi_reg_writeb(hdata, HDMI_AVI_HEADER2, infoframe->len); -		hdr_sum = infoframe->type + infoframe->ver + infoframe->len; +		hdmi_reg_writeb(hdata, HDMI_AVI_HEADER0, infoframe->any.type); +		hdmi_reg_writeb(hdata, HDMI_AVI_HEADER1, +				infoframe->any.version); +		hdmi_reg_writeb(hdata, HDMI_AVI_HEADER2, infoframe->any.length); +		hdr_sum = infoframe->any.type + infoframe->any.version + +			  infoframe->any.length;  		/* Output format zero hardcoded ,RGB YBCR selection */  		hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 0 << 5 |  			AVI_ACTIVE_FORMAT_VALID |  			AVI_UNDERSCANNED_DISPLAY_VALID); -		aspect_ratio = AVI_PIC_ASPECT_RATIO_16_9; - -		hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), aspect_ratio | -				AVI_SAME_AS_PIC_ASPECT_RATIO); +		/* +		 * Set the aspect ratio as per the mode, mentioned in +		 * Table 9 AVI InfoFrame Data Byte 2 of CEA-861-D Standard +		 */ +		switch (hdata->mode_conf.aspect_ratio) { +		case HDMI_PICTURE_ASPECT_4_3: +			hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), +					hdata->mode_conf.aspect_ratio | +					AVI_4_3_CENTER_RATIO); +			break; +		case HDMI_PICTURE_ASPECT_16_9: +			hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), +					hdata->mode_conf.aspect_ratio | +					AVI_16_9_CENTER_RATIO); +			break; +		case HDMI_PICTURE_ASPECT_NONE: +		default: +			hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(2), +					hdata->mode_conf.aspect_ratio | +					AVI_SAME_AS_PIC_ASPECT_RATIO); +			break; +		}  		vic = hdata->mode_conf.cea_video_id;  		hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), vic);  		chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1), -					infoframe->len, hdr_sum); +					infoframe->any.length, hdr_sum);  		DRM_DEBUG_KMS("AVI checksum = 0x%x\n", chksum);  		hdmi_reg_writeb(hdata, HDMI_AVI_CHECK_SUM, chksum);  		break; -	case HDMI_PACKET_TYPE_AUI: +	case HDMI_INFOFRAME_TYPE_AUDIO:  		hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02); -		hdmi_reg_writeb(hdata, HDMI_AUI_HEADER0, infoframe->type); -		hdmi_reg_writeb(hdata, HDMI_AUI_HEADER1, infoframe->ver); -		hdmi_reg_writeb(hdata, HDMI_AUI_HEADER2, infoframe->len); -		hdr_sum = infoframe->type + infoframe->ver + infoframe->len; +		hdmi_reg_writeb(hdata, HDMI_AUI_HEADER0, infoframe->any.type); +		hdmi_reg_writeb(hdata, HDMI_AUI_HEADER1, +				infoframe->any.version); +		hdmi_reg_writeb(hdata, HDMI_AUI_HEADER2, infoframe->any.length); +		hdr_sum = infoframe->any.type + infoframe->any.version + +			  infoframe->any.length;  		chksum = hdmi_chksum(hdata, HDMI_AUI_BYTE(1), -					infoframe->len, hdr_sum); +					infoframe->any.length, hdr_sum);  		DRM_DEBUG_KMS("AUI checksum = 0x%x\n", chksum);  		hdmi_reg_writeb(hdata, HDMI_AUI_CHECK_SUM, chksum);  		break; @@ -742,58 +1019,66 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata,  	}  } -static bool hdmi_is_connected(void *ctx) +static enum drm_connector_status hdmi_detect(struct drm_connector *connector, +				bool force)  { -	struct hdmi_context *hdata = ctx; +	struct hdmi_context *hdata = ctx_from_connector(connector); + +	hdata->hpd = gpio_get_value(hdata->hpd_gpio); -	return hdata->hpd; +	return hdata->hpd ? connector_status_connected : +			connector_status_disconnected;  } -static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector) +static void hdmi_connector_destroy(struct drm_connector *connector) +{ +} + +static struct drm_connector_funcs hdmi_connector_funcs = { +	.dpms = drm_helper_connector_dpms, +	.fill_modes = drm_helper_probe_single_connector_modes, +	.detect = hdmi_detect, +	.destroy = hdmi_connector_destroy, +}; + +static int hdmi_get_modes(struct drm_connector *connector)  { -	struct edid *raw_edid; -	struct hdmi_context *hdata = ctx; +	struct hdmi_context *hdata = ctx_from_connector(connector); +	struct edid *edid; -	if (!hdata->ddc_port) -		return ERR_PTR(-ENODEV); +	if (!hdata->ddc_adpt) +		return -ENODEV; -	raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); -	if (!raw_edid) -		return ERR_PTR(-ENODEV); +	edid = drm_get_edid(connector, hdata->ddc_adpt); +	if (!edid) +		return -ENODEV; -	hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); +	hdata->dvi_mode = !drm_detect_hdmi_monitor(edid);  	DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n",  		(hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"), -		raw_edid->width_cm, raw_edid->height_cm); +		edid->width_cm, edid->height_cm); + +	drm_mode_connector_update_edid_property(connector, edid); -	return raw_edid; +	return drm_add_edid_modes(connector, edid);  }  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);  	return -EINVAL;  } -static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode) +static int hdmi_mode_valid(struct drm_connector *connector, +			struct drm_display_mode *mode)  { -	struct hdmi_context *hdata = ctx; +	struct hdmi_context *hdata = ctx_from_connector(connector);  	int ret;  	DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", @@ -801,12 +1086,93 @@ static int hdmi_check_mode(void *ctx, struct drm_display_mode *mode)  		(mode->flags & DRM_MODE_FLAG_INTERLACE) ? true :  		false, mode->clock * 1000); +	ret = mixer_check_mode(mode); +	if (ret) +		return MODE_BAD; +  	ret = hdmi_find_phy_conf(hdata, mode->clock * 1000);  	if (ret < 0) +		return MODE_BAD; + +	return MODE_OK; +} + +static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector) +{ +	struct hdmi_context *hdata = ctx_from_connector(connector); + +	return hdata->encoder; +} + +static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { +	.get_modes = hdmi_get_modes, +	.mode_valid = hdmi_mode_valid, +	.best_encoder = hdmi_best_encoder, +}; + +static int hdmi_create_connector(struct exynos_drm_display *display, +			struct drm_encoder *encoder) +{ +	struct hdmi_context *hdata = display->ctx; +	struct drm_connector *connector = &hdata->connector; +	int ret; + +	hdata->encoder = encoder; +	connector->interlace_allowed = true; +	connector->polled = DRM_CONNECTOR_POLL_HPD; + +	ret = drm_connector_init(hdata->drm_dev, connector, +			&hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); +	if (ret) { +		DRM_ERROR("Failed to initialize connector with drm\n");  		return ret; +	} + +	drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); +	drm_sysfs_connector_add(connector); +	drm_mode_connector_attach_encoder(connector, encoder); +  	return 0;  } +static void hdmi_mode_fixup(struct exynos_drm_display *display, +				struct drm_connector *connector, +				const struct drm_display_mode *mode, +				struct drm_display_mode *adjusted_mode) +{ +	struct drm_display_mode *m; +	int mode_ok; + +	DRM_DEBUG_KMS("%s\n", __FILE__); + +	drm_mode_set_crtcinfo(adjusted_mode, 0); + +	mode_ok = hdmi_mode_valid(connector, adjusted_mode); + +	/* just return if user desired mode exists. */ +	if (mode_ok == MODE_OK) +		return; + +	/* +	 * otherwise, find the most suitable mode among modes and change it +	 * to adjusted_mode. +	 */ +	list_for_each_entry(m, &connector->modes, head) { +		mode_ok = hdmi_mode_valid(connector, m); + +		if (mode_ok == MODE_OK) { +			DRM_INFO("desired mode doesn't exist so\n"); +			DRM_INFO("use the most suitable mode among modes.\n"); + +			DRM_DEBUG_KMS("Adjusted Mode: [%d]x[%d] [%d]Hz\n", +				m->hdisplay, m->vdisplay, m->vrefresh); + +			drm_mode_copy(adjusted_mode, m); +			break; +		} +	} +} +  static void hdmi_set_acr(u32 freq, u8 *acr)  {  	u32 n, cts; @@ -967,25 +1333,20 @@ 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)  { -	struct hdmi_infoframe infoframe; +	union hdmi_infoframe infoframe;  	/* disable HPD interrupts from HDMI IP block, use GPIO instead */  	hdmi_reg_writemask(hdata, HDMI_INTC_CON, 0, HDMI_INTC_EN_GLOBAL | @@ -994,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); @@ -1021,14 +1384,14 @@ static void hdmi_conf_init(struct hdmi_context *hdata)  		hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);  		hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);  	} else { -		infoframe.type = HDMI_PACKET_TYPE_AVI; -		infoframe.ver = HDMI_AVI_VERSION; -		infoframe.len = HDMI_AVI_LENGTH; +		infoframe.any.type = HDMI_INFOFRAME_TYPE_AVI; +		infoframe.any.version = HDMI_AVI_VERSION; +		infoframe.any.length = HDMI_AVI_LENGTH;  		hdmi_reg_infoframe(hdata, &infoframe); -		infoframe.type = HDMI_PACKET_TYPE_AUI; -		infoframe.ver = HDMI_AUI_VERSION; -		infoframe.len = HDMI_AUI_LENGTH; +		infoframe.any.type = HDMI_INFOFRAME_TYPE_AUDIO; +		infoframe.any.version = HDMI_AUI_VERSION; +		infoframe.any.length = HDMI_AUI_LENGTH;  		hdmi_reg_infoframe(hdata, &infoframe);  		/* enable AVI packet every vsync, fixes purple line problem */ @@ -1117,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) @@ -1284,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) @@ -1330,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) { @@ -1363,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) @@ -1404,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); @@ -1435,6 +1789,7 @@ static void hdmi_v13_mode_set(struct hdmi_context *hdata,  	hdata->mode_conf.cea_video_id =  		drm_match_cea_mode((struct drm_display_mode *)m);  	hdata->mode_conf.pixel_clock = m->clock * 1000; +	hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;  	hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);  	hdmi_set_reg(core->h_v_line, 3, (m->htotal << 12) | m->vtotal); @@ -1531,6 +1886,7 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,  	hdata->mode_conf.cea_video_id =  		drm_match_cea_mode((struct drm_display_mode *)m);  	hdata->mode_conf.pixel_clock = m->clock * 1000; +	hdata->mode_conf.aspect_ratio = m->picture_aspect_ratio;  	hdmi_set_reg(core->h_blank, 2, m->htotal - m->hdisplay);  	hdmi_set_reg(core->v_line, 2, m->vtotal); @@ -1632,9 +1988,10 @@ static void hdmi_v14_mode_set(struct hdmi_context *hdata,  	hdmi_set_reg(tg->tg_3d, 1, 0x0);  } -static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode) +static void hdmi_mode_set(struct exynos_drm_display *display, +			struct drm_display_mode *mode)  { -	struct hdmi_context *hdata = ctx; +	struct hdmi_context *hdata = display->ctx;  	struct drm_display_mode *m = mode;  	DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%s\n", @@ -1642,22 +1999,18 @@ static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)  		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  		hdmi_v14_mode_set(hdata, mode);  } -static void hdmi_get_max_resol(void *ctx, unsigned int *width, -					unsigned int *height) +static void hdmi_commit(struct exynos_drm_display *display)  { -	*width = MAX_WIDTH; -	*height = MAX_HEIGHT; -} - -static void hdmi_commit(void *ctx) -{ -	struct hdmi_context *hdata = ctx; +	struct hdmi_context *hdata = display->ctx;  	mutex_lock(&hdata->hdmi_mutex);  	if (!hdata->powered) { @@ -1669,8 +2022,9 @@ static void hdmi_commit(void *ctx)  	hdmi_conf_apply(hdata);  } -static void hdmi_poweron(struct hdmi_context *hdata) +static void hdmi_poweron(struct exynos_drm_display *display)  { +	struct hdmi_context *hdata = display->ctx;  	struct hdmi_resources *res = &hdata->res;  	mutex_lock(&hdata->hdmi_mutex); @@ -1683,18 +2037,25 @@ static void hdmi_poweron(struct hdmi_context *hdata)  	mutex_unlock(&hdata->hdmi_mutex); +	pm_runtime_get_sync(hdata->dev); +  	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);  	hdmiphy_poweron(hdata); +	hdmi_commit(display);  } -static void hdmi_poweroff(struct hdmi_context *hdata) +static void hdmi_poweroff(struct exynos_drm_display *display)  { +	struct hdmi_context *hdata = display->ctx;  	struct hdmi_resources *res = &hdata->res;  	mutex_lock(&hdata->hdmi_mutex); @@ -1702,42 +2063,62 @@ static void hdmi_poweroff(struct hdmi_context *hdata)  		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); -	mutex_lock(&hdata->hdmi_mutex); +	pm_runtime_put_sync(hdata->dev); +	mutex_lock(&hdata->hdmi_mutex);  	hdata->powered = false;  out:  	mutex_unlock(&hdata->hdmi_mutex);  } -static void hdmi_dpms(void *ctx, int mode) +static void hdmi_dpms(struct exynos_drm_display *display, int mode)  { -	struct hdmi_context *hdata = ctx; +	struct hdmi_context *hdata = display->ctx; +	struct drm_encoder *encoder = hdata->encoder; +	struct drm_crtc *crtc = encoder->crtc; +	struct drm_crtc_helper_funcs *funcs = NULL;  	DRM_DEBUG_KMS("mode %d\n", mode);  	switch (mode) {  	case DRM_MODE_DPMS_ON: -		if (pm_runtime_suspended(hdata->dev)) -			pm_runtime_get_sync(hdata->dev); +		hdmi_poweron(display);  		break;  	case DRM_MODE_DPMS_STANDBY:  	case DRM_MODE_DPMS_SUSPEND:  	case DRM_MODE_DPMS_OFF: -		if (!pm_runtime_suspended(hdata->dev)) -			pm_runtime_put_sync(hdata->dev); +		/* +		 * The SFRs of VP and Mixer are updated by Vertical Sync of +		 * Timing generator which is a part of HDMI so the sequence +		 * to disable TV Subsystem should be as following, +		 *	VP -> Mixer -> HDMI +		 * +		 * Below codes will try to disable Mixer and VP(if used) +		 * prior to disabling HDMI. +		 */ +		if (crtc) +			funcs = crtc->helper_private; +		if (funcs && funcs->dpms) +			(*funcs->dpms)(crtc, mode); + +		hdmi_poweroff(display);  		break;  	default:  		DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode); @@ -1745,30 +2126,39 @@ static void hdmi_dpms(void *ctx, int mode)  	}  } -static struct exynos_hdmi_ops hdmi_ops = { -	/* display */ -	.is_connected	= hdmi_is_connected, -	.get_edid	= hdmi_get_edid, -	.check_mode	= hdmi_check_mode, - -	/* manager */ +static struct exynos_drm_display_ops hdmi_display_ops = { +	.create_connector = hdmi_create_connector, +	.mode_fixup	= hdmi_mode_fixup,  	.mode_set	= hdmi_mode_set, -	.get_max_resol	= hdmi_get_max_resol, -	.commit		= hdmi_commit,  	.dpms		= hdmi_dpms, +	.commit		= hdmi_commit,  }; -static irqreturn_t hdmi_irq_thread(int irq, void *arg) +static struct exynos_drm_display hdmi_display = { +	.type = EXYNOS_DISPLAY_TYPE_HDMI, +	.ops = &hdmi_display_ops, +}; + +static void hdmi_hotplug_work_func(struct work_struct *work)  { -	struct exynos_drm_hdmi_context *ctx = arg; -	struct hdmi_context *hdata = ctx->ctx; +	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);  	mutex_unlock(&hdata->hdmi_mutex); -	if (ctx->drm_dev) -		drm_helper_hpd_irq_event(ctx->drm_dev); +	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;  } @@ -1787,37 +2177,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;  	} @@ -1825,8 +2213,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; @@ -1834,28 +2224,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; -} - -static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy; - -void hdmi_attach_ddc_client(struct i2c_client *ddc) -{ -	if (ddc) -		hdmi_ddc = ddc; -} - -void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy) -{ -	if (hdmiphy) -		hdmi_hdmiphy = hdmiphy; +	return ret;  }  static struct s5p_hdmi_platform_data *drm_hdmi_dt_parse_pdata @@ -1885,51 +2261,110 @@ err_data:  static struct of_device_id hdmi_match_types[] = {  	{  		.compatible = "samsung,exynos5-hdmi", -		.data	= (void	*)HDMI_TYPE14, +		.data = &exynos5_hdmi_driver_data,  	}, {  		.compatible = "samsung,exynos4212-hdmi", -		.data	= (void	*)HDMI_TYPE14, +		.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 exynos_drm_hdmi_context *drm_hdmi_ctx;  	struct hdmi_context *hdata; -	struct s5p_hdmi_platform_data *pdata;  	struct resource *res; -	const struct of_device_id *match;  	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; -	pdata = drm_hdmi_dt_parse_pdata(dev); -	if (!pdata) -		return -EINVAL; +	if (!dev->of_node) { +		ret = -ENODEV; +		goto err_del_component; +	} -	drm_hdmi_ctx = devm_kzalloc(dev, sizeof(*drm_hdmi_ctx), GFP_KERNEL); -	if (!drm_hdmi_ctx) -		return -ENOMEM; +	pdata = drm_hdmi_dt_parse_pdata(dev); +	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); -	drm_hdmi_ctx->ctx = (void *)hdata; -	hdata->parent_ctx = (void *)drm_hdmi_ctx; - -	platform_set_drvdata(pdev, drm_hdmi_ctx); +	platform_set_drvdata(pdev, &hdmi_display);  	match = of_match_node(hdmi_match_types, dev->of_node); -	if (!match) -		return -ENODEV; -	hdata->type = (enum hdmi_type)match->data; +	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; @@ -1937,36 +2372,69 @@ 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 */ -	if (i2c_add_driver(&ddc_driver)) { -		DRM_ERROR("failed to register ddc i2c driver\n"); -		return -ENOENT; +	ddc_node = of_parse_phandle(dev->of_node, "ddc", 0); +	if (!ddc_node) { +		DRM_ERROR("Failed to find ddc node in device tree\n"); +		ret = -ENODEV; +		goto err_del_component;  	} -	hdata->ddc_port = hdmi_ddc; +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 -EPROBE_DEFER; +	} + +	phy_node = hdmi_legacy_phy_dt_binding(dev); +	if (phy_node) +		goto out_get_phy_port;  	/* hdmiphy i2c driver */ -	if (i2c_add_driver(&hdmiphy_driver)) { -		DRM_ERROR("failed to register hdmiphy i2c driver\n"); -		ret = -ENOENT; +	phy_node = of_parse_phandle(dev->of_node, "phy", 0); +	if (!phy_node) { +		DRM_ERROR("Failed to find hdmiphy node in device tree\n"); +		ret = -ENODEV;  		goto err_ddc;  	} -	hdata->hdmiphy_port = hdmi_hdmiphy; +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);  	if (hdata->irq < 0) { @@ -1977,114 +2445,64 @@ 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, -			"hdmi", drm_hdmi_ctx); +			"hdmi", hdata);  	if (ret) {  		DRM_ERROR("failed to register hdmi interrupt\n");  		goto err_hdmiphy;  	} -	/* Attach HDMI Driver to common hdmi. */ -	exynos_hdmi_drv_attach(drm_hdmi_ctx); - -	/* register specific callbacks to common hdmi. */ -	exynos_hdmi_ops_register(&hdmi_ops); +	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; -	return 0; +	ret = component_add(&pdev->dev, &hdmi_component_ops); +	if (ret) +		goto err_disable_pm_runtime; -err_hdmiphy: -	i2c_del_driver(&hdmiphy_driver); -err_ddc: -	i2c_del_driver(&ddc_driver);  	return ret; -} - -static int hdmi_remove(struct platform_device *pdev) -{ -	struct device *dev = &pdev->dev; +err_disable_pm_runtime:  	pm_runtime_disable(dev); -	/* hdmiphy i2c driver */ -	i2c_del_driver(&hdmiphy_driver); -	/* DDC i2c driver */ -	i2c_del_driver(&ddc_driver); - -	return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int hdmi_suspend(struct device *dev) -{ -	struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); -	struct hdmi_context *hdata = ctx->ctx; - -	disable_irq(hdata->irq); - -	hdata->hpd = false; -	if (ctx->drm_dev) -		drm_helper_hpd_irq_event(ctx->drm_dev); - -	if (pm_runtime_suspended(dev)) { -		DRM_DEBUG_KMS("Already suspended\n"); -		return 0; -	} - -	hdmi_poweroff(hdata); - -	return 0; -} - -static int hdmi_resume(struct device *dev) -{ -	struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); -	struct hdmi_context *hdata = ctx->ctx; - -	hdata->hpd = gpio_get_value(hdata->hpd_gpio); - -	enable_irq(hdata->irq); - -	if (!pm_runtime_suspended(dev)) { -		DRM_DEBUG_KMS("Already resumed\n"); -		return 0; -	} +err_hdmiphy: +	if (hdata->hdmiphy_port) +		put_device(&hdata->hdmiphy_port->dev); +err_ddc: +	put_device(&hdata->ddc_adpt->dev); -	hdmi_poweron(hdata); +err_del_component: +	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); -	return 0; +	return ret;  } -#endif -#ifdef CONFIG_PM_RUNTIME -static int hdmi_runtime_suspend(struct device *dev) +static int hdmi_remove(struct platform_device *pdev)  { -	struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); -	struct hdmi_context *hdata = ctx->ctx; +	struct hdmi_context *hdata = hdmi_display.ctx; -	hdmi_poweroff(hdata); +	cancel_delayed_work_sync(&hdata->hotplug_work); -	return 0; -} - -static int hdmi_runtime_resume(struct device *dev) -{ -	struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev); -	struct hdmi_context *hdata = ctx->ctx; +	put_device(&hdata->hdmiphy_port->dev); +	put_device(&hdata->ddc_adpt->dev); -	hdmi_poweron(hdata); +	pm_runtime_disable(&pdev->dev); +	component_del(&pdev->dev, &hdmi_component_ops); +	exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR);  	return 0;  } -#endif - -static const struct dev_pm_ops hdmi_pm_ops = { -	SET_SYSTEM_SLEEP_PM_OPS(hdmi_suspend, hdmi_resume) -	SET_RUNTIME_PM_OPS(hdmi_runtime_suspend, hdmi_runtime_resume, NULL) -};  struct platform_driver hdmi_driver = {  	.probe		= hdmi_probe, @@ -2092,7 +2510,6 @@ struct platform_driver hdmi_driver = {  	.driver		= {  		.name	= "exynos-hdmi",  		.owner	= THIS_MODULE, -		.pm	= &hdmi_pm_ops,  		.of_match_table = hdmi_match_types,  	},  };  | 
