#include "drmP.h"
#include "drm_edid.h"
#include "drm_crtc_helper.h"
+#include "drm_crtc.h"
#include "regs-hdmi.h"
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
-#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
+#include <linux/of_gpio.h>
+#include <plat/gpio-cfg.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
-#include "exynos_drm_hdmi.h"
+#include "exynos_drm_display.h"
#include "exynos_hdmi.h"
-#define MAX_WIDTH 1920
-#define MAX_HEIGHT 1080
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
+#define HOTPLUG_DEBOUNCE_MS 1100
+
struct hdmi_resources {
struct clk *hdmi;
struct clk *sclk_hdmi;
int regul_count;
};
+struct hdmi_tg_regs {
+ u8 cmd[1];
+ u8 h_fsz[2];
+ u8 hact_st[2];
+ u8 hact_sz[2];
+ u8 v_fsz[2];
+ u8 vsync[2];
+ u8 vsync2[2];
+ u8 vact_st[2];
+ u8 vact_sz[2];
+ u8 field_chg[2];
+ u8 vact_st2[2];
+ u8 vact_st3[2];
+ u8 vact_st4[2];
+ u8 vsync_top_hdmi[2];
+ u8 vsync_bot_hdmi[2];
+ u8 field_top_hdmi[2];
+ u8 field_bot_hdmi[2];
+ u8 tg_3d[1];
+};
+
+struct hdmi_core_regs {
+ u8 h_blank[2];
+ u8 v2_blank[2];
+ u8 v1_blank[2];
+ u8 v_line[2];
+ u8 h_line[2];
+ u8 hsync_pol[1];
+ u8 vsync_pol[1];
+ u8 int_pro_mode[1];
+ u8 v_blank_f0[2];
+ u8 v_blank_f1[2];
+ u8 h_sync_start[2];
+ u8 h_sync_end[2];
+ u8 v_sync_line_bef_2[2];
+ u8 v_sync_line_bef_1[2];
+ u8 v_sync_line_aft_2[2];
+ u8 v_sync_line_aft_1[2];
+ u8 v_sync_line_aft_pxl_2[2];
+ u8 v_sync_line_aft_pxl_1[2];
+ u8 v_blank_f2[2]; /* for 3D mode */
+ u8 v_blank_f3[2]; /* for 3D mode */
+ u8 v_blank_f4[2]; /* for 3D mode */
+ u8 v_blank_f5[2]; /* for 3D mode */
+ u8 v_sync_line_aft_3[2];
+ u8 v_sync_line_aft_4[2];
+ u8 v_sync_line_aft_5[2];
+ u8 v_sync_line_aft_6[2];
+ u8 v_sync_line_aft_pxl_3[2];
+ u8 v_sync_line_aft_pxl_4[2];
+ u8 v_sync_line_aft_pxl_5[2];
+ u8 v_sync_line_aft_pxl_6[2];
+ u8 vact_space_1[2];
+ u8 vact_space_2[2];
+ u8 vact_space_3[2];
+ u8 vact_space_4[2];
+ u8 vact_space_5[2];
+ u8 vact_space_6[2];
+};
+
+struct hdmi_mode_conf {
+ int pixel_clock;
+ struct hdmi_core_regs core;
+ struct hdmi_tg_regs tg;
+ u8 vic;
+};
+
struct hdmi_context {
struct device *dev;
struct drm_device *drm_dev;
struct fb_videomode *default_timing;
+ struct hdmi_mode_conf mode_conf;
unsigned int is_v13:1;
unsigned int default_win;
unsigned int default_bpp;
bool hpd_handle;
- bool enabled;
+ bool has_hdmi_sink;
+ bool has_hdmi_audio;
+ bool is_soc_exynos5;
+ bool is_hdmi_powered_on;
+ bool video_enabled;
struct resource *regs_res;
void __iomem *regs;
- unsigned int irq;
- struct workqueue_struct *wq;
- struct work_struct hotplug_work;
+ unsigned int external_irq;
+ unsigned int internal_irq;
+ unsigned int curr_irq;
+ struct timer_list hotplug_timer;
struct i2c_client *ddc_port;
struct i2c_client *hdmiphy_port;
-
+ int hpd_gpio;
/* current hdmiphy conf index */
int cur_conf;
struct hdmi_resources res;
- void *parent_ctx;
};
/* HDMI Version 1.3 */
&hdmi_v13_conf_1080p60 },
};
-/* HDMI Version 1.4 */
-static const u8 hdmiphy_conf27_027[32] = {
- 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
- 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
- 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
-};
-
-static const u8 hdmiphy_conf74_25[32] = {
- 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
- 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
- 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
-};
-
-static const u8 hdmiphy_conf148_5[32] = {
- 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
- 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
- 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
- 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
-};
-
-struct hdmi_tg_regs {
- u8 cmd;
- u8 h_fsz_l;
- u8 h_fsz_h;
- u8 hact_st_l;
- u8 hact_st_h;
- u8 hact_sz_l;
- u8 hact_sz_h;
- u8 v_fsz_l;
- u8 v_fsz_h;
- u8 vsync_l;
- u8 vsync_h;
- u8 vsync2_l;
- u8 vsync2_h;
- u8 vact_st_l;
- u8 vact_st_h;
- u8 vact_sz_l;
- u8 vact_sz_h;
- u8 field_chg_l;
- u8 field_chg_h;
- u8 vact_st2_l;
- u8 vact_st2_h;
- u8 vact_st3_l;
- u8 vact_st3_h;
- u8 vact_st4_l;
- u8 vact_st4_h;
- u8 vsync_top_hdmi_l;
- u8 vsync_top_hdmi_h;
- u8 vsync_bot_hdmi_l;
- u8 vsync_bot_hdmi_h;
- u8 field_top_hdmi_l;
- u8 field_top_hdmi_h;
- u8 field_bot_hdmi_l;
- u8 field_bot_hdmi_h;
- u8 tg_3d;
-};
-
-struct hdmi_core_regs {
- u8 h_blank[2];
- u8 v2_blank[2];
- u8 v1_blank[2];
- u8 v_line[2];
- u8 h_line[2];
- u8 hsync_pol[1];
- u8 vsync_pol[1];
- u8 int_pro_mode[1];
- u8 v_blank_f0[2];
- u8 v_blank_f1[2];
- u8 h_sync_start[2];
- u8 h_sync_end[2];
- u8 v_sync_line_bef_2[2];
- u8 v_sync_line_bef_1[2];
- u8 v_sync_line_aft_2[2];
- u8 v_sync_line_aft_1[2];
- u8 v_sync_line_aft_pxl_2[2];
- u8 v_sync_line_aft_pxl_1[2];
- u8 v_blank_f2[2]; /* for 3D mode */
- u8 v_blank_f3[2]; /* for 3D mode */
- u8 v_blank_f4[2]; /* for 3D mode */
- u8 v_blank_f5[2]; /* for 3D mode */
- u8 v_sync_line_aft_3[2];
- u8 v_sync_line_aft_4[2];
- u8 v_sync_line_aft_5[2];
- u8 v_sync_line_aft_6[2];
- u8 v_sync_line_aft_pxl_3[2];
- u8 v_sync_line_aft_pxl_4[2];
- u8 v_sync_line_aft_pxl_5[2];
- u8 v_sync_line_aft_pxl_6[2];
- u8 vact_space_1[2];
- u8 vact_space_2[2];
- u8 vact_space_3[2];
- u8 vact_space_4[2];
- u8 vact_space_5[2];
- u8 vact_space_6[2];
-};
-
-struct hdmi_preset_conf {
- struct hdmi_core_regs core;
- struct hdmi_tg_regs tg;
+struct hdmiphy_config {
+ int pixel_clock;
+ u8 conf[32];
};
-struct hdmi_conf {
- int width;
- int height;
- int vrefresh;
- bool interlace;
- const u8 *hdmiphy_data;
- const struct hdmi_preset_conf *conf;
-};
-
-static const struct hdmi_preset_conf hdmi_conf_480p60 = {
- .core = {
- .h_blank = {0x8a, 0x00},
- .v2_blank = {0x0d, 0x02},
- .v1_blank = {0x2d, 0x00},
- .v_line = {0x0d, 0x02},
- .h_line = {0x5a, 0x03},
- .hsync_pol = {0x01},
- .vsync_pol = {0x01},
- .int_pro_mode = {0x00},
- .v_blank_f0 = {0xff, 0xff},
- .v_blank_f1 = {0xff, 0xff},
- .h_sync_start = {0x0e, 0x00},
- .h_sync_end = {0x4c, 0x00},
- .v_sync_line_bef_2 = {0x0f, 0x00},
- .v_sync_line_bef_1 = {0x09, 0x00},
- .v_sync_line_aft_2 = {0xff, 0xff},
- .v_sync_line_aft_1 = {0xff, 0xff},
- .v_sync_line_aft_pxl_2 = {0xff, 0xff},
- .v_sync_line_aft_pxl_1 = {0xff, 0xff},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- .vact_space_1 = {0xff, 0xff},
- .vact_space_2 = {0xff, 0xff},
- .vact_space_3 = {0xff, 0xff},
- .vact_space_4 = {0xff, 0xff},
- .vact_space_5 = {0xff, 0xff},
- .vact_space_6 = {0xff, 0xff},
- /* other don't care */
+static const struct hdmiphy_config phy_configs[] = {
+ {
+ .pixel_clock = 25200000,
+ .conf = {
+ 0x01, 0x51, 0x2A, 0x75, 0x40, 0x01, 0x00, 0x08,
+ 0x82, 0x80, 0xfc, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xf4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0x5a, 0x03, /* h_fsz */
- 0x8a, 0x00, 0xd0, 0x02, /* hact */
- 0x0d, 0x02, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x2d, 0x00, 0xe0, 0x01, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x48, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
+ {
+ .pixel_clock = 27000000,
+ .conf = {
+ 0x01, 0xd1, 0x22, 0x51, 0x40, 0x08, 0xfc, 0x20,
+ 0x98, 0xa0, 0xcb, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x06, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xe4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
},
-};
-
-static const struct hdmi_preset_conf hdmi_conf_720p50 = {
- .core = {
- .h_blank = {0xbc, 0x02},
- .v2_blank = {0xee, 0x02},
- .v1_blank = {0x1e, 0x00},
- .v_line = {0xee, 0x02},
- .h_line = {0xbc, 0x07},
- .hsync_pol = {0x00},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f0 = {0xff, 0xff},
- .v_blank_f1 = {0xff, 0xff},
- .h_sync_start = {0xb6, 0x01},
- .h_sync_end = {0xde, 0x01},
- .v_sync_line_bef_2 = {0x0a, 0x00},
- .v_sync_line_bef_1 = {0x05, 0x00},
- .v_sync_line_aft_2 = {0xff, 0xff},
- .v_sync_line_aft_1 = {0xff, 0xff},
- .v_sync_line_aft_pxl_2 = {0xff, 0xff},
- .v_sync_line_aft_pxl_1 = {0xff, 0xff},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- .vact_space_1 = {0xff, 0xff},
- .vact_space_2 = {0xff, 0xff},
- .vact_space_3 = {0xff, 0xff},
- .vact_space_4 = {0xff, 0xff},
- .vact_space_5 = {0xff, 0xff},
- .vact_space_6 = {0xff, 0xff},
- /* other don't care */
+ {
+ .pixel_clock = 27027000,
+ .conf = {
+ 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
+ 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0xbc, 0x07, /* h_fsz */
- 0xbc, 0x02, 0x00, 0x05, /* hact */
- 0xee, 0x02, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x1e, 0x00, 0xd0, 0x02, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x48, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
- },
-};
-
-static const struct hdmi_preset_conf hdmi_conf_720p60 = {
- .core = {
- .h_blank = {0x72, 0x01},
- .v2_blank = {0xee, 0x02},
- .v1_blank = {0x1e, 0x00},
- .v_line = {0xee, 0x02},
- .h_line = {0x72, 0x06},
- .hsync_pol = {0x00},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f0 = {0xff, 0xff},
- .v_blank_f1 = {0xff, 0xff},
- .h_sync_start = {0x6c, 0x00},
- .h_sync_end = {0x94, 0x00},
- .v_sync_line_bef_2 = {0x0a, 0x00},
- .v_sync_line_bef_1 = {0x05, 0x00},
- .v_sync_line_aft_2 = {0xff, 0xff},
- .v_sync_line_aft_1 = {0xff, 0xff},
- .v_sync_line_aft_pxl_2 = {0xff, 0xff},
- .v_sync_line_aft_pxl_1 = {0xff, 0xff},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- .vact_space_1 = {0xff, 0xff},
- .vact_space_2 = {0xff, 0xff},
- .vact_space_3 = {0xff, 0xff},
- .vact_space_4 = {0xff, 0xff},
- .vact_space_5 = {0xff, 0xff},
- .vact_space_6 = {0xff, 0xff},
- /* other don't care */
+ {
+ .pixel_clock = 36000000,
+ .conf = {
+ 0x01, 0x51, 0x2d, 0x55, 0x40, 0x01, 0x00, 0x08,
+ 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xab, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0x72, 0x06, /* h_fsz */
- 0x72, 0x01, 0x00, 0x05, /* hact */
- 0xee, 0x02, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x1e, 0x00, 0xd0, 0x02, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x48, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
+ {
+ .pixel_clock = 40000000,
+ .conf = {
+ 0x01, 0x51, 0x32, 0x55, 0x40, 0x01, 0x00, 0x08,
+ 0x82, 0x80, 0x2c, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x9a, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
},
-};
-
-static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
- .core = {
- .h_blank = {0xd0, 0x02},
- .v2_blank = {0x32, 0x02},
- .v1_blank = {0x16, 0x00},
- .v_line = {0x65, 0x04},
- .h_line = {0x50, 0x0a},
- .hsync_pol = {0x00},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x01},
- .v_blank_f0 = {0x49, 0x02},
- .v_blank_f1 = {0x65, 0x04},
- .h_sync_start = {0x0e, 0x02},
- .h_sync_end = {0x3a, 0x02},
- .v_sync_line_bef_2 = {0x07, 0x00},
- .v_sync_line_bef_1 = {0x02, 0x00},
- .v_sync_line_aft_2 = {0x39, 0x02},
- .v_sync_line_aft_1 = {0x34, 0x02},
- .v_sync_line_aft_pxl_2 = {0x38, 0x07},
- .v_sync_line_aft_pxl_1 = {0x38, 0x07},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- .vact_space_1 = {0xff, 0xff},
- .vact_space_2 = {0xff, 0xff},
- .vact_space_3 = {0xff, 0xff},
- .vact_space_4 = {0xff, 0xff},
- .vact_space_5 = {0xff, 0xff},
- .vact_space_6 = {0xff, 0xff},
- /* other don't care */
+ {
+ .pixel_clock = 65000000,
+ .conf = {
+ 0x01, 0xd1, 0x36, 0x34, 0x40, 0x1e, 0x0a, 0x08,
+ 0x82, 0xa0, 0x45, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xbd, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0x50, 0x0a, /* h_fsz */
- 0xd0, 0x02, 0x80, 0x07, /* hact */
- 0x65, 0x04, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x16, 0x00, 0x1c, 0x02, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x49, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
+ {
+ .pixel_clock = 74176000,
+ .conf = {
+ 0x01, 0xd1, 0x3e, 0x35, 0x40, 0x5b, 0xde, 0x08,
+ 0x82, 0xa0, 0x73, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x56, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xa6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
},
-};
-
-static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
- .core = {
- .h_blank = {0x18, 0x01},
- .v2_blank = {0x32, 0x02},
- .v1_blank = {0x16, 0x00},
- .v_line = {0x65, 0x04},
- .h_line = {0x98, 0x08},
- .hsync_pol = {0x00},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x01},
- .v_blank_f0 = {0x49, 0x02},
- .v_blank_f1 = {0x65, 0x04},
- .h_sync_start = {0x56, 0x00},
- .h_sync_end = {0x82, 0x00},
- .v_sync_line_bef_2 = {0x07, 0x00},
- .v_sync_line_bef_1 = {0x02, 0x00},
- .v_sync_line_aft_2 = {0x39, 0x02},
- .v_sync_line_aft_1 = {0x34, 0x02},
- .v_sync_line_aft_pxl_2 = {0xa4, 0x04},
- .v_sync_line_aft_pxl_1 = {0xa4, 0x04},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- .vact_space_1 = {0xff, 0xff},
- .vact_space_2 = {0xff, 0xff},
- .vact_space_3 = {0xff, 0xff},
- .vact_space_4 = {0xff, 0xff},
- .vact_space_5 = {0xff, 0xff},
- .vact_space_6 = {0xff, 0xff},
- /* other don't care */
+ {
+ .pixel_clock = 74250000,
+ .conf = {
+ 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
+ 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0x98, 0x08, /* h_fsz */
- 0x18, 0x01, 0x80, 0x07, /* hact */
- 0x65, 0x04, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x16, 0x00, 0x1c, 0x02, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x49, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
+ {
+ .pixel_clock = 83500000,
+ .conf = {
+ 0x01, 0xd1, 0x23, 0x11, 0x40, 0x0c, 0xfb, 0x08,
+ 0x85, 0xa0, 0xd1, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x93, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
},
-};
-
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
- .core = {
- .h_blank = {0xd0, 0x02},
- .v2_blank = {0x65, 0x04},
- .v1_blank = {0x2d, 0x00},
- .v_line = {0x65, 0x04},
- .h_line = {0x50, 0x0a},
- .hsync_pol = {0x00},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f0 = {0xff, 0xff},
- .v_blank_f1 = {0xff, 0xff},
- .h_sync_start = {0x0e, 0x02},
- .h_sync_end = {0x3a, 0x02},
- .v_sync_line_bef_2 = {0x09, 0x00},
- .v_sync_line_bef_1 = {0x04, 0x00},
- .v_sync_line_aft_2 = {0xff, 0xff},
- .v_sync_line_aft_1 = {0xff, 0xff},
- .v_sync_line_aft_pxl_2 = {0xff, 0xff},
- .v_sync_line_aft_pxl_1 = {0xff, 0xff},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- .vact_space_1 = {0xff, 0xff},
- .vact_space_2 = {0xff, 0xff},
- .vact_space_3 = {0xff, 0xff},
- .vact_space_4 = {0xff, 0xff},
- .vact_space_5 = {0xff, 0xff},
- .vact_space_6 = {0xff, 0xff},
- /* other don't care */
+ {
+ .pixel_clock = 106500000,
+ .conf = {
+ 0x01, 0xd1, 0x2c, 0x12, 0x40, 0x0c, 0x09, 0x08,
+ 0x84, 0xa0, 0x0a, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0x50, 0x0a, /* h_fsz */
- 0xd0, 0x02, 0x80, 0x07, /* hact */
- 0x65, 0x04, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x2d, 0x00, 0x38, 0x04, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x48, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
+ {
+ .pixel_clock = 108000000,
+ .conf = {
+ 0x01, 0x51, 0x2d, 0x15, 0x40, 0x01, 0x00, 0x08,
+ 0x82, 0x80, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xc7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
},
-};
-
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
- .core = {
- .h_blank = {0x18, 0x01},
- .v2_blank = {0x65, 0x04},
- .v1_blank = {0x2d, 0x00},
- .v_line = {0x65, 0x04},
- .h_line = {0x98, 0x08},
- .hsync_pol = {0x00},
- .vsync_pol = {0x00},
- .int_pro_mode = {0x00},
- .v_blank_f0 = {0xff, 0xff},
- .v_blank_f1 = {0xff, 0xff},
- .h_sync_start = {0x56, 0x00},
- .h_sync_end = {0x82, 0x00},
- .v_sync_line_bef_2 = {0x09, 0x00},
- .v_sync_line_bef_1 = {0x04, 0x00},
- .v_sync_line_aft_2 = {0xff, 0xff},
- .v_sync_line_aft_1 = {0xff, 0xff},
- .v_sync_line_aft_pxl_2 = {0xff, 0xff},
- .v_sync_line_aft_pxl_1 = {0xff, 0xff},
- .v_blank_f2 = {0xff, 0xff},
- .v_blank_f3 = {0xff, 0xff},
- .v_blank_f4 = {0xff, 0xff},
- .v_blank_f5 = {0xff, 0xff},
- .v_sync_line_aft_3 = {0xff, 0xff},
- .v_sync_line_aft_4 = {0xff, 0xff},
- .v_sync_line_aft_5 = {0xff, 0xff},
- .v_sync_line_aft_6 = {0xff, 0xff},
- .v_sync_line_aft_pxl_3 = {0xff, 0xff},
- .v_sync_line_aft_pxl_4 = {0xff, 0xff},
- .v_sync_line_aft_pxl_5 = {0xff, 0xff},
- .v_sync_line_aft_pxl_6 = {0xff, 0xff},
- /* other don't care */
+ {
+ .pixel_clock = 146250000,
+ .conf = {
+ 0x01, 0xd1, 0x3d, 0x15, 0x40, 0x18, 0xfd, 0x08,
+ 0x83, 0xa0, 0x6e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x50, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
},
- .tg = {
- 0x00, /* cmd */
- 0x98, 0x08, /* h_fsz */
- 0x18, 0x01, 0x80, 0x07, /* hact */
- 0x65, 0x04, /* v_fsz */
- 0x01, 0x00, 0x33, 0x02, /* vsync */
- 0x2d, 0x00, 0x38, 0x04, /* vact */
- 0x33, 0x02, /* field_chg */
- 0x48, 0x02, /* vact_st2 */
- 0x00, 0x00, /* vact_st3 */
- 0x00, 0x00, /* vact_st4 */
- 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
- 0x01, 0x00, 0x33, 0x02, /* field top/bot */
- 0x00, /* 3d FP */
+ {
+ .pixel_clock = 148500000,
+ .conf = {
+ 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
+ 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+ },
},
};
-static const struct hdmi_conf hdmi_confs[] = {
- { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 },
- { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 },
- { 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
- { 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 },
- { 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 },
- { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
- { 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 },
+struct hdmi_infoframe {
+ enum HDMI_PACKET_TYPE type;
+ u8 ver;
+ u8 len;
};
-
static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
{
return readl(hdata->regs + reg_id);
writel(value, hdata->regs + reg_id);
}
+static void hdmi_cfg_hpd(struct hdmi_context *hdata, bool external)
+{
+ if (external) {
+ s3c_gpio_cfgpin(hdata->hpd_gpio, S3C_GPIO_SFN(0xf));
+ s3c_gpio_setpull(hdata->hpd_gpio, S3C_GPIO_PULL_DOWN);
+ } else {
+ s3c_gpio_cfgpin(hdata->hpd_gpio, S3C_GPIO_SFN(3));
+ s3c_gpio_setpull(hdata->hpd_gpio, S3C_GPIO_PULL_NONE);
+ }
+}
+
+static int hdmi_get_hpd(struct hdmi_context *hdata)
+{
+ return gpio_get_value(hdata->hpd_gpio);
+}
+
static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
{
#define DUMPREG(reg_id) \
int i;
for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
- if (hdmi_v13_confs[i].width == mode->hdisplay &&
- hdmi_v13_confs[i].height == mode->vdisplay &&
- hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
- hdmi_v13_confs[i].interlace ==
- ((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
- true : false))
- return i;
-
- return -EINVAL;
-}
-
-static int hdmi_v14_conf_index(struct drm_display_mode *mode)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
- if (hdmi_confs[i].width == mode->hdisplay &&
- hdmi_confs[i].height == mode->vdisplay &&
- hdmi_confs[i].vrefresh == mode->vrefresh &&
- hdmi_confs[i].interlace ==
+ if (hdmi_v13_confs[i].width == mode->crtc_hdisplay &&
+ hdmi_v13_confs[i].height == mode->crtc_vdisplay &&
+ hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
+ hdmi_v13_confs[i].interlace ==
((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
true : false))
return i;
return -EINVAL;
}
-static int hdmi_conf_index(struct hdmi_context *hdata,
- struct drm_display_mode *mode)
-{
- if (hdata->is_v13)
- return hdmi_v13_conf_index(mode);
-
- return hdmi_v14_conf_index(mode);
-}
-
static bool hdmi_is_connected(void *ctx)
{
struct hdmi_context *hdata = ctx;
- u32 val = hdmi_reg_read(hdata, HDMI_HPD_STATUS);
-
- if (val)
- return true;
+ if (hdata->is_hdmi_powered_on) {
+ if (!hdmi_reg_read(hdata, HDMI_HPD_STATUS)) {
+ DRM_DEBUG_KMS("hdmi is not connected\n");
+ return false;
+ }
+ } else if (!hdmi_get_hpd(hdata)) {
+ DRM_DEBUG_KMS("hdmi is not connected\n");
+ return false;
+ }
- return false;
+ return true;
}
-static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
- u8 *edid, int len)
+static struct edid *hdmi_get_edid(void *ctx, struct drm_connector *connector)
{
- struct edid *raw_edid;
struct hdmi_context *hdata = ctx;
+ struct edid *edid;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", DRM_BASE_ID(connector),
+ drm_get_connector_name(connector));
if (!hdata->ddc_port)
- return -ENODEV;
-
- raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter);
- if (raw_edid) {
- memcpy(edid, raw_edid, min((1 + raw_edid->extensions)
- * EDID_LENGTH, len));
- DRM_DEBUG_KMS("width[%d] x height[%d]\n",
- raw_edid->width_cm, raw_edid->height_cm);
- } else {
- return -ENODEV;
- }
+ return ERR_PTR(-ENODEV);
- return 0;
+ edid = drm_get_edid(connector, hdata->ddc_port->adapter);
+ if (!edid)
+ return ERR_PTR(-ENODEV);
+
+ /*
+ * TODO : Need to call this in exynos_drm_connector.c, do a drm_get_edid
+ * to get the edid and then call drm_detect_hdmi_monitor.
+ */
+ hdata->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
+ hdata->has_hdmi_audio = drm_detect_monitor_audio(edid);
+ DRM_DEBUG_KMS("%s monitor\n", hdata->has_hdmi_sink ? "hdmi" : "dvi");
+
+ return edid;
}
static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
return -EINVAL;
}
-static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
+static int find_hdmiphy_conf(int pixel_clock)
{
int i;
- DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
- check_timing->xres, check_timing->yres,
- check_timing->refresh, (check_timing->vmode &
- FB_VMODE_INTERLACED) ? true : false);
+ for (i = 0; i < ARRAY_SIZE(phy_configs); i++) {
+ if (phy_configs[i].pixel_clock == pixel_clock)
+ return i;
+ }
+ DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
+ return -EINVAL;
+}
- for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++)
- if (hdmi_confs[i].width == check_timing->xres &&
- hdmi_confs[i].height == check_timing->yres &&
- hdmi_confs[i].vrefresh == check_timing->refresh &&
- hdmi_confs[i].interlace ==
- ((check_timing->vmode & FB_VMODE_INTERLACED) ?
- true : false))
- return 0;
+static int hdmi_v14_check_timing(struct fb_videomode *mode)
+{
+ int ret;
+ enum exynos_mixer_mode_type mode_type;
- /* TODO */
+ /* Make sure the mixer can generate this mode */
+ mode_type = exynos_mixer_get_mode_type(mode->xres, mode->yres);
+ if (mode_type == EXYNOS_MIXER_MODE_INVALID)
+ return -EINVAL;
- return -EINVAL;
+ ret = find_hdmiphy_conf(mode->pixclock);
+ return ret < 0 ? ret : 0;
+}
+
+static u8 hdmi_chksum(struct hdmi_context *hdata,
+ u32 start, u8 len, u32 hdr_sum)
+{
+ int i;
+ /* hdr_sum : header0 + header1 + header2
+ * start : start address of packet byte1
+ * len : packet bytes - 1 */
+ for (i = 0; i < len; ++i)
+ hdr_sum += hdmi_reg_read(hdata, start + i * 4);
+
+ return (u8)(0x100 - (hdr_sum & 0xff));
+}
+
+void hdmi_reg_infoframe(struct hdmi_context *hdata,
+ struct hdmi_infoframe *infoframe)
+{
+ u32 hdr_sum;
+ u8 chksum;
+ u32 aspect_ratio;
+ u32 mod;
+
+ /* TODO: stringify HDMI_PACKET_TYPE */
+ DRM_DEBUG_KMS("type: %d ver: %d len: %d\n", infoframe->type,
+ infoframe->ver, infoframe->len);
+ mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
+ if (!hdata->has_hdmi_sink) {
+ hdmi_reg_writeb(hdata, HDMI_VSI_CON,
+ HDMI_VSI_CON_DO_NOT_TRANSMIT);
+ hdmi_reg_writeb(hdata, HDMI_AVI_CON,
+ HDMI_AVI_CON_DO_NOT_TRANSMIT);
+ hdmi_reg_writeb(hdata, HDMI_AUI_CON, HDMI_AUI_CON_NO_TRAN);
+ return;
+ }
+
+ switch (infoframe->type) {
+
+ case HDMI_PACKET_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;
+ /* 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);
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(4), hdata->mode_conf.vic);
+
+ chksum = hdmi_chksum(hdata, HDMI_AVI_BYTE(1),
+ infoframe->len, 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:
+ 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;
+ chksum = hdmi_chksum(hdata, HDMI_AUI_BYTE(1),
+ infoframe->len, hdr_sum);
+ DRM_DEBUG_KMS("AUI checksum = 0x%x\n", chksum);
+ hdmi_reg_writeb(hdata, HDMI_AUI_CHECK_SUM, chksum);
+ break;
+
+ default:
+ break;
+ }
}
static int hdmi_check_timing(void *ctx, void *timing)
struct hdmi_context *hdata = ctx;
struct fb_videomode *check_timing = timing;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
check_timing->yres, check_timing->refresh,
check_timing->vmode);
return hdmi_v14_check_timing(check_timing);
}
-static int hdmi_display_power_on(void *ctx, int mode)
-{
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- DRM_DEBUG_KMS("hdmi [on]\n");
- break;
- case DRM_MODE_DPMS_STANDBY:
- break;
- case DRM_MODE_DPMS_SUSPEND:
- break;
- case DRM_MODE_DPMS_OFF:
- DRM_DEBUG_KMS("hdmi [off]\n");
- break;
- default:
- break;
- }
-
- return 0;
-}
-
static void hdmi_set_acr(u32 freq, u8 *acr)
{
u32 n, cts;
static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
{
- u32 mod;
-
- mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
- if (mod & HDMI_DVI_MODE_EN)
+ if (!hdata->has_hdmi_audio)
return;
hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
else
reg = HDMI_CORE_RSTOUT;
- /* resetting HDMI core */
- hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT);
- mdelay(10);
- hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
- mdelay(10);
+ /* resetting HDMI core */
+ hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+ hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
+ mdelay(10);
+
+ /* enable hpd handle for drm */
+ hdata->hpd_handle = true;
+}
+
+static void hdmi_enable_video(struct hdmi_context *hdata)
+{
+ if (hdata->is_v13)
+ return;
+
+ hdata->video_enabled = true;
+ hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+}
+
+static void hdmi_disable_video(struct hdmi_context *hdata)
+{
+ if (hdata->is_v13)
+ return;
- /* enable hpd handle for drm */
- hdata->hpd_handle = true;
+ /* Set the blue screen color to black */
+ hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_R_0, 0);
+ hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_R_1, 0);
+ hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_G_0, 0);
+ hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_G_1, 0);
+ hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_B_0, 0);
+ hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_B_1, 0);
+
+ /* Enable the "blue screen", which effectively disconnects the mixer */
+ hdata->video_enabled = false;
+ hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_BLUE_SCR_EN);
}
static void hdmi_conf_init(struct hdmi_context *hdata)
{
+ struct hdmi_infoframe infoframe;
/* disable hpd handle for drm */
hdata->hpd_handle = false;
/* choose HDMI mode */
hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
- /* disable bluescreen */
- hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
+
+ if (hdata->video_enabled)
+ hdmi_enable_video(hdata);
+ else
+ hdmi_disable_video(hdata);
+
+ if (!hdata->has_hdmi_sink) {
+ /* choose DVI mode */
+ hdmi_reg_writemask(hdata, HDMI_MODE_SEL,
+ HDMI_MODE_DVI_EN, HDMI_MODE_MASK);
+ hdmi_reg_writeb(hdata, HDMI_CON_2,
+ HDMI_VID_PREAMBLE_DIS | HDMI_GUARD_BAND_DIS);
+ }
if (hdata->is_v13) {
/* choose bluescreen (fecal) color */
hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
} else {
/* enable AVI packet every vsync, fixes purple line problem */
- hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5);
hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
+
+ infoframe.type = HDMI_PACKET_TYPE_AVI;
+ infoframe.ver = HDMI_AVI_VERSION;
+ infoframe.len = HDMI_AVI_LENGTH;
+ hdmi_reg_infoframe(hdata, &infoframe);
+
+ infoframe.type = HDMI_PACKET_TYPE_AUI;
+ infoframe.ver = HDMI_AUI_VERSION;
+ infoframe.len = HDMI_AUI_LENGTH;
+ hdmi_reg_infoframe(hdata, &infoframe);
+
}
/* enable hpd handle for drm */
static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
{
- const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf;
- const struct hdmi_core_regs *core = &conf->core;
- const struct hdmi_tg_regs *tg = &conf->tg;
+ struct hdmi_core_regs *core = &hdata->mode_conf.core;
+ struct hdmi_tg_regs *tg = &hdata->mode_conf.tg;
int tries;
- /* setting core registers */
hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]);
hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]);
hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]);
- /* Timing generator registers */
- hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
- hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
- hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
- hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
- hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
- hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
- hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
- hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d);
+ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi[0]);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi[1]);
+ hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d[0]);
/* waiting for HDMIPHY's PLL to get to steady state */
for (tries = 100; tries; --tries) {
int ret;
int i;
+ DRM_DEBUG_KMS("\n");
+
if (!hdata->hdmiphy_port) {
DRM_ERROR("hdmiphy is not attached\n");
return;
}
/* pixel clock */
- if (hdata->is_v13)
+ if (hdata->is_v13) {
hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data;
- else
- hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data;
+ } else {
+ i = find_hdmiphy_conf(hdata->mode_conf.pixel_clock);
+ hdmiphy_data = phy_configs[i].conf;
+ }
memcpy(buffer, hdmiphy_data, 32);
ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
static void hdmi_conf_apply(struct hdmi_context *hdata)
{
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("\n");
hdmiphy_conf_reset(hdata);
hdmiphy_conf_apply(hdata);
hdmi_conf_reset(hdata);
hdmi_conf_init(hdata);
- hdmi_audio_init(hdata);
+ if (!hdata->is_soc_exynos5)
+ hdmi_audio_init(hdata);
/* setting core registers */
hdmi_timing_apply(hdata);
- hdmi_audio_control(hdata, true);
+ if (!hdata->is_soc_exynos5)
+ hdmi_audio_control(hdata, true);
hdmi_regs_dump(hdata, "start");
}
+static void hdmi_mode_copy(struct drm_display_mode *dst,
+ struct drm_display_mode *src)
+{
+ struct drm_mode_object base;
+
+ DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(src), src->name);
+
+ /* following information should be preserved,
+ * required for releasing the drm_display_mode node,
+ * duplicated to recieve adjustment info. */
+
+ base.id = dst->base.id;
+ base.type = dst->base.type;
+
+ memcpy(dst, src, sizeof(struct drm_display_mode));
+
+ dst->base.id = base.id;
+ dst->base.type = base.type;
+}
+
static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
- struct drm_display_mode *m;
+ struct drm_display_mode *t, *con_mode;
struct hdmi_context *hdata = ctx;
int index;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- drm_mode_set_crtcinfo(adjusted_mode, 0);
-
- if (hdata->is_v13)
- index = hdmi_v13_conf_index(adjusted_mode);
- else
- index = hdmi_v14_conf_index(adjusted_mode);
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] [MODE:%d:%s]\n",
+ DRM_BASE_ID(connector),
+ drm_get_connector_name(connector),
+ DRM_BASE_ID(mode), mode->name);
- /* just return if user desired mode exists. */
- if (index >= 0)
- return;
+ /*
+ * Match the incoming mode to a mode in the connector list and copy it
+ * over. This is important since this might be an adjusted mode from the
+ * mixer and those have differing crtc_* values.
+ */
+ list_for_each_entry_safe(con_mode, t, &connector->modes, head) {
+ if (mode->hdisplay == con_mode->hdisplay &&
+ mode->vdisplay == con_mode->vdisplay &&
+ mode->clock == con_mode->clock) {
+ hdmi_mode_copy(adjusted_mode, con_mode);
+ return;
+ }
+ }
/*
- * otherwise, find the most suitable mode among modes and change it
- * to adjusted_mode.
+ * We didn't find a mode which matched the desired resolution, so just
+ * find something with the same clock.
*/
- list_for_each_entry(m, &connector->modes, head) {
+ list_for_each_entry_safe(con_mode, t, &connector->modes, head) {
if (hdata->is_v13)
- index = hdmi_v13_conf_index(m);
+ index = hdmi_v13_conf_index(con_mode);
else
- index = hdmi_v14_conf_index(m);
+ index = find_hdmiphy_conf(con_mode->clock * 1000);
if (index >= 0) {
DRM_INFO("desired mode doesn't exist so\n");
DRM_INFO("use the most suitable mode among modes.\n");
- memcpy(adjusted_mode, m, sizeof(*m));
+ hdmi_mode_copy(adjusted_mode, con_mode);
break;
}
}
}
-static void hdmi_mode_set(void *ctx, void *mode)
+static void hdmi_set_reg(u8 *reg_pair, int num_bytes, u32 value)
+{
+ int i;
+ BUG_ON(num_bytes > 4);
+ for (i = 0; i < num_bytes; i++)
+ reg_pair[i] = (value >> (8 * i)) & 0xff;
+}
+
+static void hdmi_v14_mode_set(struct hdmi_context *hdata,
+ struct drm_display_mode *m)
+{
+ struct hdmi_core_regs *core = &hdata->mode_conf.core;
+ struct hdmi_tg_regs *tg = &hdata->mode_conf.tg;
+
+ DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(m), m->name);
+
+ hdata->mode_conf.vic = drm_match_cea_mode(m);
+
+ hdata->mode_conf.pixel_clock = m->clock * 1000;
+ hdmi_set_reg(core->h_blank, 2, m->crtc_htotal - m->crtc_hdisplay);
+ hdmi_set_reg(core->v_line, 2, m->crtc_vtotal);
+ hdmi_set_reg(core->h_line, 2, m->crtc_htotal);
+ hdmi_set_reg(core->hsync_pol, 1,
+ (m->flags & DRM_MODE_FLAG_NHSYNC) ? 1 : 0);
+ hdmi_set_reg(core->vsync_pol, 1,
+ (m->flags & DRM_MODE_FLAG_NVSYNC) ? 1 : 0);
+ hdmi_set_reg(core->int_pro_mode, 1,
+ (m->flags & DRM_MODE_FLAG_INTERLACE) ? 1 : 0);
+
+ /*
+ * Quirk requirement for exynos 5 HDMI IP design,
+ * 2 pixels less than the actual calculation for hsync_start
+ * and end.
+ */
+
+ /* Following values & calculations differ for different type of modes */
+ if (m->flags & DRM_MODE_FLAG_INTERLACE) {
+ /* Interlaced Mode */
+ hdmi_set_reg(core->v_sync_line_bef_2, 2,
+ (m->crtc_vsync_end - m->crtc_vdisplay) / 2);
+ hdmi_set_reg(core->v_sync_line_bef_1, 2,
+ (m->crtc_vsync_start - m->crtc_vdisplay) / 2);
+ hdmi_set_reg(core->v2_blank, 2, m->crtc_vtotal / 2);
+ hdmi_set_reg(core->v1_blank, 2,
+ (m->crtc_vtotal - m->crtc_vdisplay) / 2);
+ hdmi_set_reg(core->v_blank_f0, 2,
+ (m->crtc_vtotal +
+ ((m->crtc_vsync_end - m->crtc_vsync_start) * 4) + 5)
+ / 2);
+ hdmi_set_reg(core->v_blank_f1, 2, m->crtc_vtotal);
+ hdmi_set_reg(core->v_sync_line_aft_2, 2,
+ (m->crtc_vtotal / 2) + 7);
+ hdmi_set_reg(core->v_sync_line_aft_1, 2,
+ (m->crtc_vtotal / 2) + 2);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2,
+ (m->crtc_htotal / 2) +
+ (m->crtc_hsync_start - m->crtc_hdisplay));
+ hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2,
+ (m->crtc_htotal / 2) +
+ (m->crtc_hsync_start - m->crtc_hdisplay));
+ hdmi_set_reg(tg->vact_st, 2,
+ (m->crtc_vtotal - m->crtc_vdisplay) / 2);
+ hdmi_set_reg(tg->vact_sz, 2, m->crtc_vdisplay / 2);
+ hdmi_set_reg(tg->vact_st2, 2, 0x249);/* Reset value + 1*/
+ hdmi_set_reg(tg->vact_st3, 2, 0x0);
+ hdmi_set_reg(tg->vact_st4, 2, 0x0);
+ } else {
+ /* Progressive Mode */
+ hdmi_set_reg(core->v_sync_line_bef_2, 2,
+ m->crtc_vsync_end - m->crtc_vdisplay);
+ hdmi_set_reg(core->v_sync_line_bef_1, 2,
+ m->crtc_vsync_start - m->crtc_vdisplay);
+ hdmi_set_reg(core->v2_blank, 2, m->crtc_vtotal);
+ hdmi_set_reg(core->v1_blank, 2,
+ m->crtc_vtotal - m->crtc_vdisplay);
+ hdmi_set_reg(core->v_blank_f0, 2, 0xffff);
+ hdmi_set_reg(core->v_blank_f1, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_2, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_1, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_2, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_1, 2, 0xffff);
+ hdmi_set_reg(tg->vact_st, 2, m->crtc_vtotal - m->crtc_vdisplay);
+ hdmi_set_reg(tg->vact_sz, 2, m->crtc_vdisplay);
+ hdmi_set_reg(tg->vact_st2, 2, 0x248); /* Reset value */
+ hdmi_set_reg(tg->vact_st3, 2, 0x47b); /* Reset value */
+ hdmi_set_reg(tg->vact_st4, 2, 0x6ae); /* Reset value */
+ }
+
+ /* Following values & calculations are same irrespective of mode type */
+ hdmi_set_reg(core->h_sync_start, 2,
+ m->crtc_hsync_start - m->crtc_hdisplay - 2);
+ hdmi_set_reg(core->h_sync_end, 2,
+ m->crtc_hsync_end - m->crtc_hdisplay - 2);
+ hdmi_set_reg(core->vact_space_1, 2, 0xffff);
+ hdmi_set_reg(core->vact_space_2, 2, 0xffff);
+ hdmi_set_reg(core->vact_space_3, 2, 0xffff);
+ hdmi_set_reg(core->vact_space_4, 2, 0xffff);
+ hdmi_set_reg(core->vact_space_5, 2, 0xffff);
+ hdmi_set_reg(core->vact_space_6, 2, 0xffff);
+ hdmi_set_reg(core->v_blank_f2, 2, 0xffff);
+ hdmi_set_reg(core->v_blank_f3, 2, 0xffff);
+ hdmi_set_reg(core->v_blank_f4, 2, 0xffff);
+ hdmi_set_reg(core->v_blank_f5, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_3, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_4, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_5, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_6, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_3, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_4, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_5, 2, 0xffff);
+ hdmi_set_reg(core->v_sync_line_aft_pxl_6, 2, 0xffff);
+
+ /* Timing generator registers */
+ hdmi_set_reg(tg->cmd, 1, 0x0);
+ hdmi_set_reg(tg->h_fsz, 2, m->crtc_htotal);
+ hdmi_set_reg(tg->hact_st, 2, m->crtc_htotal - m->crtc_hdisplay);
+ hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay);
+ hdmi_set_reg(tg->v_fsz, 2, m->crtc_vtotal);
+ hdmi_set_reg(tg->vsync, 2, 0x1);
+ hdmi_set_reg(tg->vsync2, 2, 0x233); /* Reset value */
+ hdmi_set_reg(tg->field_chg, 2, 0x233); /* Reset value */
+ hdmi_set_reg(tg->vsync_top_hdmi, 2, 0x1); /* Reset value */
+ hdmi_set_reg(tg->vsync_bot_hdmi, 2, 0x233); /* Reset value */
+ hdmi_set_reg(tg->field_top_hdmi, 2, 0x1); /* Reset value */
+ hdmi_set_reg(tg->field_bot_hdmi, 2, 0x233); /* Reset value */
+ hdmi_set_reg(tg->tg_3d, 1, 0x0);
+
+ /* Workaround 4 implementation for 1440x900 resolution support */
+ if (hdata->is_soc_exynos5) {
+ if (m->crtc_hdisplay == 1440 && m->crtc_vdisplay == 900 &&
+ m->clock == 106500) {
+ hdmi_set_reg(tg->hact_st, 2,
+ m->crtc_htotal - m->crtc_hdisplay - 0xe0);
+ hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay + 0xe0);
+ }
+
+ /* Workaround 3 implementation for 800x600 resolution support */
+ if (m->hdisplay == 800 && m->vdisplay == 600
+ && m->clock == 40000) {
+ hdmi_set_reg(tg->hact_st, 2,
+ m->crtc_htotal - m->crtc_hdisplay - 0x20);
+ hdmi_set_reg(tg->hact_sz, 2, m->crtc_hdisplay + 0x20);
+ }
+ }
+}
+
+static void hdmi_mode_set(void *ctx, struct drm_display_mode *mode)
{
struct hdmi_context *hdata = ctx;
- int conf_idx;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[MODE:%d:%s]\n", DRM_BASE_ID(mode), mode->name);
- conf_idx = hdmi_conf_index(hdata, mode);
- if (conf_idx >= 0)
- hdata->cur_conf = conf_idx;
+ if (hdata->is_v13)
+ hdata->cur_conf = hdmi_v13_conf_index(mode);
else
- DRM_DEBUG_KMS("not supported mode\n");
+ hdmi_v14_mode_set(hdata, mode);
+}
+
+static int hdmiphy_update_bits(struct i2c_client *client, u8 *reg_cache,
+ u8 reg, u8 mask, u8 val)
+{
+ int ret;
+ u8 buffer[2];
+
+ buffer[0] = reg;
+ buffer[1] = (reg_cache[reg] & ~mask) | (val & mask);
+ reg_cache[reg] = buffer[1];
+
+ ret = i2c_master_send(client, buffer, 2);
+ if (ret != 2)
+ return -EIO;
+
+ return 0;
}
-static void hdmi_get_max_resol(void *ctx, unsigned int *width,
- unsigned int *height)
+static int hdmiphy_s_power(struct i2c_client *client, bool on)
{
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ u8 reg_cache[32] = { 0 };
+ u8 buffer[2];
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", on ? "on" : "off");
+
+ /* Cache all 32 registers to make the code below faster */
+ buffer[0] = 0x0;
+ ret = i2c_master_send(client, buffer, 1);
+ if (ret != 1) {
+ ret = -EIO;
+ goto exit;
+ }
+ ret = i2c_master_recv(client, reg_cache, 32);
+ if (ret != 32) {
+ ret = -EIO;
+ goto exit;
+ }
+
+ /* Go to/from configuration from/to operation mode */
+ ret = hdmiphy_update_bits(client, reg_cache, 0x1f, 0xff,
+ on ? 0x80 : 0x00);
+ if (ret)
+ goto exit;
+
+ /*
+ * Turn off undocumented "oscpad" if !on; it turns on again in
+ * hdmiphy_conf_apply()
+ */
+ if (!on)
+ ret = hdmiphy_update_bits(client, reg_cache, 0x0b, 0xc0, 0x00);
+ if (ret)
+ goto exit;
+
+ /* Disable powerdown if on; enable if !on */
+ ret = hdmiphy_update_bits(client, reg_cache, 0x1d, 0x80,
+ on ? 0 : ~0);
+ if (ret)
+ goto exit;
+ ret = hdmiphy_update_bits(client, reg_cache, 0x1d, 0x77,
+ on ? 0 : ~0);
+ if (ret)
+ goto exit;
+
+ /*
+ * Turn off bit 3 of reg 4 if !on; it turns on again in
+ * hdmiphy_conf_apply(). It's unclear what this bit does.
+ */
+ if (!on)
+ ret = hdmiphy_update_bits(client, reg_cache, 0x04, BIT(3), 0);
+ if (ret)
+ goto exit;
+
+exit:
+ /* Don't expect any errors so just do a single warn */
+ WARN_ON(ret);
- *width = MAX_WIDTH;
- *height = MAX_HEIGHT;
+ return ret;
}
-static void hdmi_commit(void *ctx)
+static void hdmi_resource_poweron(struct hdmi_context *hdata)
{
- struct hdmi_context *hdata = ctx;
+ struct hdmi_resources *res = &hdata->res;
+
+ hdata->is_hdmi_powered_on = true;
+ hdmi_cfg_hpd(hdata, false);
+
+ /* irq change by TV power status */
+ if (hdata->curr_irq == hdata->internal_irq)
+ return;
+
+ disable_irq(hdata->curr_irq);
+
+ hdata->curr_irq = hdata->internal_irq;
+
+ enable_irq(hdata->curr_irq);
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ /* turn HDMI power on */
+ regulator_bulk_enable(res->regul_count, res->regul_bulk);
+
+ /* power-on hdmi clocks */
+ clk_enable(res->hdmiphy);
+ hdmiphy_s_power(hdata->hdmiphy_port, 1);
+ hdmiphy_conf_reset(hdata);
+ hdmi_conf_reset(hdata);
+ hdmi_conf_init(hdata);
+ if (!hdata->is_soc_exynos5)
+ hdmi_audio_init(hdata);
hdmi_conf_apply(hdata);
+}
+
+static void hdmi_resource_poweroff(struct hdmi_context *hdata)
+{
+ struct hdmi_resources *res = &hdata->res;
+
+ hdmi_cfg_hpd(hdata, true);
+
+ if (hdata->curr_irq == hdata->external_irq)
+ return;
+
+ disable_irq(hdata->curr_irq);
+ hdata->curr_irq = hdata->external_irq;
- hdata->enabled = true;
+ enable_irq(hdata->curr_irq);
+ hdata->is_hdmi_powered_on = false;
+
+ hdmiphy_s_power(hdata->hdmiphy_port, 0);
+ hdmiphy_conf_reset(hdata);
+
+ /* power-off hdmi clocks */
+ clk_disable(res->hdmiphy);
+
+ /* turn HDMI power off */
+ regulator_bulk_disable(res->regul_count, res->regul_bulk);
}
-static void hdmi_disable(void *ctx)
+static int hdmi_dpms(void *ctx, int mode)
{
struct hdmi_context *hdata = ctx;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[DPMS:%s]\n", drm_get_dpms_name(mode));
- if (hdata->enabled) {
- hdmi_audio_control(hdata, false);
- hdmiphy_conf_reset(hdata);
- hdmi_conf_reset(hdata);
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ if (!hdata->is_hdmi_powered_on)
+ hdmi_resource_poweron(hdata);
+ hdmi_enable_video(hdata);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ hdmi_disable_video(hdata);
+ break;
+ case DRM_MODE_DPMS_OFF:
+ case DRM_MODE_DPMS_SUSPEND:
+ if (hdata->is_hdmi_powered_on)
+ hdmi_resource_poweroff(hdata);
+ break;
+ default:
+ DRM_DEBUG_KMS("unknown dpms mode: %d\n", mode);
+ break;
}
+
+ return 0;
+}
+
+static int hdmi_subdrv_probe(void *ctx, struct drm_device *drm_dev)
+{
+ struct hdmi_context *hdata = ctx;
+
+ DRM_DEBUG_KMS("[DEV:%s]\n", drm_dev->devname);
+
+ hdata->drm_dev = drm_dev;
+
+ return 0;
}
-static struct exynos_hdmi_ops hdmi_ops = {
+static struct exynos_panel_ops hdmi_ops = {
/* display */
+ .subdrv_probe = hdmi_subdrv_probe,
.is_connected = hdmi_is_connected,
.get_edid = hdmi_get_edid,
.check_timing = hdmi_check_timing,
- .power_on = hdmi_display_power_on,
+ .dpms = hdmi_dpms,
/* manager */
.mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
- .get_max_resol = hdmi_get_max_resol,
- .commit = hdmi_commit,
- .disable = hdmi_disable,
};
/*
* Handle hotplug events outside the interrupt handler proper.
*/
-static void hdmi_hotplug_func(struct work_struct *work)
+static void hdmi_hotplug_timer_func(unsigned long data)
{
- struct hdmi_context *hdata =
- container_of(work, struct hdmi_context, hotplug_work);
- struct exynos_drm_hdmi_context *ctx =
- (struct exynos_drm_hdmi_context *)hdata->parent_ctx;
+ struct hdmi_context *hdata = (struct hdmi_context *)data;
- drm_helper_hpd_irq_event(ctx->drm_dev);
+ drm_helper_hpd_irq_event(hdata->drm_dev);
}
static irqreturn_t hdmi_irq_handler(int irq, void *arg)
{
- struct exynos_drm_hdmi_context *ctx = arg;
- struct hdmi_context *hdata = ctx->ctx;
- u32 intc_flag;
-
- intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
- /* clearing flags for HPD plug/unplug */
- if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
- DRM_DEBUG_KMS("unplugged, handling:%d\n", hdata->hpd_handle);
- hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
- HDMI_INTC_FLAG_HPD_UNPLUG);
- }
- if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
- DRM_DEBUG_KMS("plugged, handling:%d\n", hdata->hpd_handle);
- hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
- HDMI_INTC_FLAG_HPD_PLUG);
+ struct hdmi_context *hdata = arg;
+ u32 intc_flag, time_ms = HOTPLUG_DEBOUNCE_MS;
+
+ if (hdata->is_hdmi_powered_on) {
+ intc_flag = hdmi_reg_read(hdata, HDMI_INTC_FLAG);
+ /* clearing flags for HPD plug/unplug */
+ if (intc_flag & HDMI_INTC_FLAG_HPD_UNPLUG) {
+ DRM_DEBUG_KMS("int unplugged, handling:%d\n",
+ hdata->hpd_handle);
+ hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_UNPLUG);
+ }
+ if (intc_flag & HDMI_INTC_FLAG_HPD_PLUG) {
+ DRM_DEBUG_KMS("int plugged, handling:%d\n",
+ hdata->hpd_handle);
+ hdmi_reg_writemask(hdata, HDMI_INTC_FLAG, ~0,
+ HDMI_INTC_FLAG_HPD_PLUG);
+ }
+
+ /*
+ * No need to debounce if we're powered on, the hardware
+ * has already done it for us.
+ */
+ time_ms = 0;
}
- if (ctx->drm_dev && hdata->hpd_handle)
- queue_work(hdata->wq, &hdata->hotplug_work);
+ if (hdata->drm_dev && hdata->hpd_handle)
+ mod_timer(&hdata->hotplug_timer,
+ jiffies + msecs_to_jiffies(time_ms));
return IRQ_HANDLED;
}
{
struct device *dev = hdata->dev;
struct hdmi_resources *res = &hdata->res;
+#ifndef CONFIG_ARCH_EXYNOS5
static char *supply[] = {
"hdmi-en",
"vdd",
"vdd_pll",
};
int i, ret;
+#endif
DRM_DEBUG_KMS("HDMI resource init\n");
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
+#ifndef CONFIG_ARCH_EXYNOS5
res->regul_bulk = kzalloc(ARRAY_SIZE(supply) *
sizeof res->regul_bulk[0], GFP_KERNEL);
if (!res->regul_bulk) {
goto fail;
}
res->regul_count = ARRAY_SIZE(supply);
+#endif
+ /* TODO:
+ * These clocks also should be added in
+ * runtime resume and runtime suspend
+ */
+ clk_enable(res->hdmi);
+ clk_enable(res->sclk_hdmi);
return 0;
fail:
return 0;
}
-static void hdmi_resource_poweron(struct hdmi_context *hdata)
-{
- struct hdmi_resources *res = &hdata->res;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- /* turn HDMI power on */
- regulator_bulk_enable(res->regul_count, res->regul_bulk);
- /* power-on hdmi physical interface */
- clk_enable(res->hdmiphy);
- /* turn clocks on */
- clk_enable(res->hdmi);
- clk_enable(res->sclk_hdmi);
-
- hdmiphy_conf_reset(hdata);
- hdmi_conf_reset(hdata);
- hdmi_conf_init(hdata);
- hdmi_audio_init(hdata);
-}
-
-static void hdmi_resource_poweroff(struct hdmi_context *hdata)
-{
- struct hdmi_resources *res = &hdata->res;
-
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
-
- /* turn clocks off */
- clk_disable(res->sclk_hdmi);
- clk_disable(res->hdmi);
- /* power-off hdmiphy */
- clk_disable(res->hdmiphy);
- /* turn HDMI power off */
- regulator_bulk_disable(res->regul_count, res->regul_bulk);
-}
+struct platform_device *hdmi_audio_device;
-static int hdmi_runtime_suspend(struct device *dev)
+int hdmi_register_audio_device(struct platform_device *pdev)
{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
-
- DRM_DEBUG_KMS("%s\n", __func__);
+ struct hdmi_context *hdata = platform_get_drvdata(pdev);
+ struct platform_device *audio_dev;
+ int ret;
- hdmi_resource_poweroff(ctx->ctx);
+ DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
- return 0;
-}
+ audio_dev = platform_device_alloc("exynos-hdmi-audio", -1);
+ if (!audio_dev) {
+ DRM_ERROR("hdmi audio device allocation failed.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
-static int hdmi_runtime_resume(struct device *dev)
-{
- struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
+ ret = platform_device_add_resources(audio_dev, pdev->resource,
+ pdev->num_resources);
+ if (ret) {
+ ret = -ENOMEM;
+ goto err_device;
+ }
- DRM_DEBUG_KMS("%s\n", __func__);
+ audio_dev->dev.of_node = of_get_next_child(pdev->dev.of_node, NULL);
+ audio_dev->dev.platform_data = (void *)hdata->hpd_gpio;
- hdmi_resource_poweron(ctx->ctx);
+ ret = platform_device_add(audio_dev);
+ if (ret) {
+ DRM_ERROR("hdmi audio device add failed.\n");
+ goto err_device;
+ }
+ hdmi_audio_device = audio_dev;
return 0;
-}
-
-static const struct dev_pm_ops hdmi_pm_ops = {
- .runtime_suspend = hdmi_runtime_suspend,
- .runtime_resume = hdmi_runtime_resume,
-};
-static struct i2c_client *hdmi_ddc, *hdmi_hdmiphy;
+err_device:
+ platform_device_put(audio_dev);
-void hdmi_attach_ddc_client(struct i2c_client *ddc)
-{
- if (ddc)
- hdmi_ddc = ddc;
+err:
+ return ret;
}
-void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
+void hdmi_unregister_audio_device(void)
{
- if (hdmiphy)
- hdmi_hdmiphy = hdmiphy;
+ DRM_DEBUG_KMS("\n");
+ platform_device_unregister(hdmi_audio_device);
}
static int __devinit hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct exynos_drm_hdmi_context *drm_hdmi_ctx;
struct hdmi_context *hdata;
struct exynos_drm_hdmi_pdata *pdata;
struct resource *res;
+ struct device_node *ddc_node, *phy_node;
int ret;
+ enum of_gpio_flags flags;
- DRM_DEBUG_KMS("[%d]\n", __LINE__);
+ DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
pdata = pdev->dev.platform_data;
if (!pdata) {
return -EINVAL;
}
- drm_hdmi_ctx = kzalloc(sizeof(*drm_hdmi_ctx), GFP_KERNEL);
- if (!drm_hdmi_ctx) {
- DRM_ERROR("failed to allocate common hdmi context.\n");
- return -ENOMEM;
- }
-
hdata = kzalloc(sizeof(struct hdmi_context), GFP_KERNEL);
if (!hdata) {
DRM_ERROR("out of memory\n");
- kfree(drm_hdmi_ctx);
return -ENOMEM;
}
- drm_hdmi_ctx->ctx = (void *)hdata;
- hdata->parent_ctx = (void *)drm_hdmi_ctx;
-
- platform_set_drvdata(pdev, drm_hdmi_ctx);
+ platform_set_drvdata(pdev, hdata);
hdata->is_v13 = pdata->is_v13;
hdata->default_win = pdata->default_win;
hdata->default_timing = &pdata->timing;
hdata->default_bpp = pdata->bpp;
hdata->dev = dev;
+ hdata->is_soc_exynos5 = of_device_is_compatible(dev->of_node,
+ "samsung,exynos5-hdmi");
ret = hdmi_resources_init(hdata);
if (ret) {
}
/* DDC i2c driver */
- if (i2c_add_driver(&ddc_driver)) {
- DRM_ERROR("failed to register ddc i2c driver\n");
- ret = -ENOENT;
+ ddc_node = of_find_node_by_name(NULL, "exynos_ddc");
+ if (!ddc_node) {
+ DRM_ERROR("Failed to find ddc node in device tree\n");
+ ret = -ENODEV;
+ goto err_iomap;
+ }
+ hdata->ddc_port = of_find_i2c_device_by_node(ddc_node);
+ if (!hdata->ddc_port) {
+ DRM_ERROR("Failed to get ddc i2c client by node\n");
+ ret = -ENODEV;
goto err_iomap;
}
-
- hdata->ddc_port = hdmi_ddc;
/* hdmiphy i2c driver */
- if (i2c_add_driver(&hdmiphy_driver)) {
- DRM_ERROR("failed to register hdmiphy i2c driver\n");
- ret = -ENOENT;
+ phy_node = of_find_node_by_name(NULL, "exynos_hdmiphy");
+ if (!phy_node) {
+ DRM_ERROR("Failed to find hdmiphy node in device tree\n");
+ 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;
}
-
- hdata->hdmiphy_port = hdmi_hdmiphy;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
goto err_hdmiphy;
}
- /* create workqueue and hotplug work */
- hdata->wq = alloc_workqueue("exynos-drm-hdmi",
- WQ_UNBOUND | WQ_NON_REENTRANT, 1);
- if (hdata->wq == NULL) {
- DRM_ERROR("Failed to create workqueue.\n");
- ret = -ENOMEM;
+ hdata->internal_irq = res->start;
+
+ hdata->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
+ "hpd-gpio", 0, &flags);
+
+ if (!gpio_is_valid(hdata->hpd_gpio)) {
+ DRM_ERROR("failed to get hpd gpio.");
+ ret = -EINVAL;
+ goto err_hdmiphy;
+ }
+
+ hdata->external_irq = gpio_to_irq(hdata->hpd_gpio);
+
+ ret = request_irq(hdata->internal_irq, hdmi_irq_handler,
+ IRQF_SHARED, "int_hdmi", hdata);
+ if (ret) {
+ DRM_ERROR("request int interrupt failed.\n");
goto err_hdmiphy;
}
- INIT_WORK(&hdata->hotplug_work, hdmi_hotplug_func);
+ disable_irq(hdata->internal_irq);
- /* register hpd interrupt */
- ret = request_irq(res->start, hdmi_irq_handler, 0, "drm_hdmi",
- drm_hdmi_ctx);
+ ret = request_irq(hdata->external_irq, hdmi_irq_handler,
+ IRQ_TYPE_EDGE_BOTH | IRQF_SHARED, "ext_hdmi",
+ hdata);
if (ret) {
- DRM_ERROR("request interrupt failed.\n");
- goto err_workqueue;
+ DRM_ERROR("request ext interrupt failed.\n");
+ goto err_int_irq;
}
- hdata->irq = res->start;
+ disable_irq(hdata->external_irq);
- /* register specific callbacks to common hdmi. */
- exynos_hdmi_ops_register(&hdmi_ops);
+ setup_timer(&hdata->hotplug_timer, hdmi_hotplug_timer_func,
+ (unsigned long)hdata);
+
+ if (of_device_is_compatible(dev->of_node,
+ "samsung,exynos5-hdmi")) {
+ ret = hdmi_register_audio_device(pdev);
+ if (ret) {
+ DRM_ERROR("hdmi-audio device registering failed.\n");
+ goto err_ext_irq;
+ }
+ }
hdmi_resource_poweron(hdata);
+ if (!hdmi_is_connected(hdata)) {
+ hdmi_resource_poweroff(hdata);
+ DRM_DEBUG_KMS("gpio state is low. powering off!\n");
+ }
+
+ exynos_display_attach_panel(EXYNOS_DRM_DISPLAY_TYPE_MIXER, &hdmi_ops,
+ hdata);
+
return 0;
-err_workqueue:
- destroy_workqueue(hdata->wq);
+err_ext_irq:
+ free_irq(hdata->external_irq, hdata);
+err_int_irq:
+ free_irq(hdata->internal_irq, hdata);
err_hdmiphy:
- i2c_del_driver(&hdmiphy_driver);
+ put_device(&hdata->hdmiphy_port->dev);
err_ddc:
- i2c_del_driver(&ddc_driver);
+ put_device(&hdata->ddc_port->dev);
err_iomap:
iounmap(hdata->regs);
err_req_region:
hdmi_resources_cleanup(hdata);
err_data:
kfree(hdata);
- kfree(drm_hdmi_ctx);
return ret;
}
static int __devexit hdmi_remove(struct platform_device *pdev)
{
- struct exynos_drm_hdmi_context *ctx = platform_get_drvdata(pdev);
- struct hdmi_context *hdata = ctx->ctx;
+ struct hdmi_context *hdata = platform_get_drvdata(pdev);
+ struct hdmi_resources *res = &hdata->res;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("[PDEV:%s]\n", pdev->name);
hdmi_resource_poweroff(hdata);
- disable_irq(hdata->irq);
- free_irq(hdata->irq, hdata);
+ hdmi_unregister_audio_device();
+
+ disable_irq(hdata->curr_irq);
+ free_irq(hdata->internal_irq, hdata);
+ free_irq(hdata->external_irq, hdata);
- cancel_work_sync(&hdata->hotplug_work);
- destroy_workqueue(hdata->wq);
+ del_timer(&hdata->hotplug_timer);
+ clk_disable(res->hdmi);
+ clk_disable(res->sclk_hdmi);
hdmi_resources_cleanup(hdata);
iounmap(hdata->regs);
release_mem_region(hdata->regs_res->start,
resource_size(hdata->regs_res));
- /* hdmiphy i2c driver */
- i2c_del_driver(&hdmiphy_driver);
- /* DDC i2c driver */
- i2c_del_driver(&ddc_driver);
+ put_device(&hdata->hdmiphy_port->dev);
+ put_device(&hdata->ddc_port->dev);
kfree(hdata);
.probe = hdmi_probe,
.remove = __devexit_p(hdmi_remove),
.driver = {
- .name = "exynos4-hdmi",
+#ifdef CONFIG_ARCH_EXYNOS5
+ .name = "exynos5-hdmi",
+#else
+ .name = "exynos4-hdmi",
+#endif
.owner = THIS_MODULE,
- .pm = &hdmi_pm_ops,
},
};