diff options
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_hdmi.c | 126 |
1 files changed, 85 insertions, 41 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 1a06a54a901f..0097032cd350 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c | |||
@@ -45,9 +45,8 @@ | |||
45 | #include <linux/gpio.h> | 45 | #include <linux/gpio.h> |
46 | #include <media/s5p_hdmi.h> | 46 | #include <media/s5p_hdmi.h> |
47 | 47 | ||
48 | #define MAX_WIDTH 1920 | ||
49 | #define MAX_HEIGHT 1080 | ||
50 | #define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev)) | 48 | #define get_hdmi_display(dev) platform_get_drvdata(to_platform_device(dev)) |
49 | #define ctx_from_connector(c) container_of(c, struct hdmi_context, connector) | ||
51 | 50 | ||
52 | /* AVI header and aspect ratio */ | 51 | /* AVI header and aspect ratio */ |
53 | #define HDMI_AVI_VERSION 0x02 | 52 | #define HDMI_AVI_VERSION 0x02 |
@@ -172,6 +171,8 @@ struct hdmi_conf_regs { | |||
172 | struct hdmi_context { | 171 | struct hdmi_context { |
173 | struct device *dev; | 172 | struct device *dev; |
174 | struct drm_device *drm_dev; | 173 | struct drm_device *drm_dev; |
174 | struct drm_connector connector; | ||
175 | struct drm_encoder *encoder; | ||
175 | bool hpd; | 176 | bool hpd; |
176 | bool powered; | 177 | bool powered; |
177 | bool dvi_mode; | 178 | bool dvi_mode; |
@@ -790,42 +791,46 @@ static void hdmi_reg_infoframe(struct hdmi_context *hdata, | |||
790 | } | 791 | } |
791 | } | 792 | } |
792 | 793 | ||
793 | static int hdmi_initialize(struct exynos_drm_display *display, | 794 | static enum drm_connector_status hdmi_detect(struct drm_connector *connector, |
794 | struct drm_device *drm_dev) | 795 | bool force) |
795 | { | 796 | { |
796 | struct hdmi_context *hdata = display->ctx; | 797 | struct hdmi_context *hdata = ctx_from_connector(connector); |
797 | 798 | ||
798 | hdata->drm_dev = drm_dev; | 799 | return hdata->hpd ? connector_status_connected : |
799 | 800 | connector_status_disconnected; | |
800 | return 0; | ||
801 | } | 801 | } |
802 | 802 | ||
803 | static bool hdmi_is_connected(struct exynos_drm_display *display) | 803 | static void hdmi_connector_destroy(struct drm_connector *connector) |
804 | { | 804 | { |
805 | struct hdmi_context *hdata = display->ctx; | ||
806 | |||
807 | return hdata->hpd; | ||
808 | } | 805 | } |
809 | 806 | ||
810 | static struct edid *hdmi_get_edid(struct exynos_drm_display *display, | 807 | static struct drm_connector_funcs hdmi_connector_funcs = { |
811 | struct drm_connector *connector) | 808 | .dpms = drm_helper_connector_dpms, |
809 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
810 | .detect = hdmi_detect, | ||
811 | .destroy = hdmi_connector_destroy, | ||
812 | }; | ||
813 | |||
814 | static int hdmi_get_modes(struct drm_connector *connector) | ||
812 | { | 815 | { |
813 | struct edid *raw_edid; | 816 | struct hdmi_context *hdata = ctx_from_connector(connector); |
814 | struct hdmi_context *hdata = display->ctx; | 817 | struct edid *edid; |
815 | 818 | ||
816 | if (!hdata->ddc_port) | 819 | if (!hdata->ddc_port) |
817 | return ERR_PTR(-ENODEV); | 820 | return -ENODEV; |
818 | 821 | ||
819 | raw_edid = drm_get_edid(connector, hdata->ddc_port->adapter); | 822 | edid = drm_get_edid(connector, hdata->ddc_port->adapter); |
820 | if (!raw_edid) | 823 | if (!edid) |
821 | return ERR_PTR(-ENODEV); | 824 | return -ENODEV; |
822 | 825 | ||
823 | hdata->dvi_mode = !drm_detect_hdmi_monitor(raw_edid); | 826 | hdata->dvi_mode = !drm_detect_hdmi_monitor(edid); |
824 | DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n", | 827 | DRM_DEBUG_KMS("%s : width[%d] x height[%d]\n", |
825 | (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"), | 828 | (hdata->dvi_mode ? "dvi monitor" : "hdmi monitor"), |
826 | raw_edid->width_cm, raw_edid->height_cm); | 829 | edid->width_cm, edid->height_cm); |
830 | |||
831 | drm_mode_connector_update_edid_property(connector, edid); | ||
827 | 832 | ||
828 | return raw_edid; | 833 | return drm_add_edid_modes(connector, edid); |
829 | } | 834 | } |
830 | 835 | ||
831 | static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) | 836 | static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) |
@@ -850,10 +855,10 @@ static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) | |||
850 | return -EINVAL; | 855 | return -EINVAL; |
851 | } | 856 | } |
852 | 857 | ||
853 | static int hdmi_check_mode(struct exynos_drm_display *display, | 858 | static int hdmi_mode_valid(struct drm_connector *connector, |
854 | struct drm_display_mode *mode) | 859 | struct drm_display_mode *mode) |
855 | { | 860 | { |
856 | struct hdmi_context *hdata = display->ctx; | 861 | struct hdmi_context *hdata = ctx_from_connector(connector); |
857 | int ret; | 862 | int ret; |
858 | 863 | ||
859 | DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", | 864 | DRM_DEBUG_KMS("xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n", |
@@ -863,11 +868,60 @@ static int hdmi_check_mode(struct exynos_drm_display *display, | |||
863 | 868 | ||
864 | ret = mixer_check_mode(mode); | 869 | ret = mixer_check_mode(mode); |
865 | if (ret) | 870 | if (ret) |
866 | return ret; | 871 | return MODE_BAD; |
867 | 872 | ||
868 | ret = hdmi_find_phy_conf(hdata, mode->clock * 1000); | 873 | ret = hdmi_find_phy_conf(hdata, mode->clock * 1000); |
869 | if (ret < 0) | 874 | if (ret < 0) |
875 | return MODE_BAD; | ||
876 | |||
877 | return MODE_OK; | ||
878 | } | ||
879 | |||
880 | static struct drm_encoder *hdmi_best_encoder(struct drm_connector *connector) | ||
881 | { | ||
882 | struct hdmi_context *hdata = ctx_from_connector(connector); | ||
883 | |||
884 | return hdata->encoder; | ||
885 | } | ||
886 | |||
887 | static struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { | ||
888 | .get_modes = hdmi_get_modes, | ||
889 | .mode_valid = hdmi_mode_valid, | ||
890 | .best_encoder = hdmi_best_encoder, | ||
891 | }; | ||
892 | |||
893 | static int hdmi_create_connector(struct exynos_drm_display *display, | ||
894 | struct drm_encoder *encoder) | ||
895 | { | ||
896 | struct hdmi_context *hdata = display->ctx; | ||
897 | struct drm_connector *connector = &hdata->connector; | ||
898 | int ret; | ||
899 | |||
900 | hdata->encoder = encoder; | ||
901 | connector->interlace_allowed = true; | ||
902 | connector->polled = DRM_CONNECTOR_POLL_HPD; | ||
903 | |||
904 | ret = drm_connector_init(hdata->drm_dev, connector, | ||
905 | &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); | ||
906 | if (ret) { | ||
907 | DRM_ERROR("Failed to initialize connector with drm\n"); | ||
870 | return ret; | 908 | return ret; |
909 | } | ||
910 | |||
911 | drm_connector_helper_add(connector, &hdmi_connector_helper_funcs); | ||
912 | drm_sysfs_connector_add(connector); | ||
913 | drm_mode_connector_attach_encoder(connector, encoder); | ||
914 | |||
915 | return 0; | ||
916 | } | ||
917 | |||
918 | static int hdmi_initialize(struct exynos_drm_display *display, | ||
919 | struct drm_device *drm_dev) | ||
920 | { | ||
921 | struct hdmi_context *hdata = display->ctx; | ||
922 | |||
923 | hdata->drm_dev = drm_dev; | ||
924 | |||
871 | return 0; | 925 | return 0; |
872 | } | 926 | } |
873 | 927 | ||
@@ -883,10 +937,10 @@ static void hdmi_mode_fixup(struct exynos_drm_display *display, | |||
883 | 937 | ||
884 | drm_mode_set_crtcinfo(adjusted_mode, 0); | 938 | drm_mode_set_crtcinfo(adjusted_mode, 0); |
885 | 939 | ||
886 | mode_ok = hdmi_check_mode(display, adjusted_mode); | 940 | mode_ok = hdmi_mode_valid(connector, adjusted_mode); |
887 | 941 | ||
888 | /* just return if user desired mode exists. */ | 942 | /* just return if user desired mode exists. */ |
889 | if (mode_ok == 0) | 943 | if (mode_ok == MODE_OK) |
890 | return; | 944 | return; |
891 | 945 | ||
892 | /* | 946 | /* |
@@ -894,9 +948,9 @@ static void hdmi_mode_fixup(struct exynos_drm_display *display, | |||
894 | * to adjusted_mode. | 948 | * to adjusted_mode. |
895 | */ | 949 | */ |
896 | list_for_each_entry(m, &connector->modes, head) { | 950 | list_for_each_entry(m, &connector->modes, head) { |
897 | mode_ok = hdmi_check_mode(display, m); | 951 | mode_ok = hdmi_mode_valid(connector, m); |
898 | 952 | ||
899 | if (mode_ok == 0) { | 953 | if (mode_ok == MODE_OK) { |
900 | DRM_INFO("desired mode doesn't exist so\n"); | 954 | DRM_INFO("desired mode doesn't exist so\n"); |
901 | DRM_INFO("use the most suitable mode among modes.\n"); | 955 | DRM_INFO("use the most suitable mode among modes.\n"); |
902 | 956 | ||
@@ -1753,13 +1807,6 @@ static void hdmi_mode_set(struct exynos_drm_display *display, | |||
1753 | hdmi_v14_mode_set(hdata, mode); | 1807 | hdmi_v14_mode_set(hdata, mode); |
1754 | } | 1808 | } |
1755 | 1809 | ||
1756 | static void hdmi_get_max_resol(struct exynos_drm_display *display, | ||
1757 | unsigned int *width, unsigned int *height) | ||
1758 | { | ||
1759 | *width = MAX_WIDTH; | ||
1760 | *height = MAX_HEIGHT; | ||
1761 | } | ||
1762 | |||
1763 | static void hdmi_commit(struct exynos_drm_display *display) | 1810 | static void hdmi_commit(struct exynos_drm_display *display) |
1764 | { | 1811 | { |
1765 | struct hdmi_context *hdata = display->ctx; | 1812 | struct hdmi_context *hdata = display->ctx; |
@@ -1854,10 +1901,7 @@ static void hdmi_dpms(struct exynos_drm_display *display, int mode) | |||
1854 | 1901 | ||
1855 | static struct exynos_drm_display_ops hdmi_display_ops = { | 1902 | static struct exynos_drm_display_ops hdmi_display_ops = { |
1856 | .initialize = hdmi_initialize, | 1903 | .initialize = hdmi_initialize, |
1857 | .is_connected = hdmi_is_connected, | 1904 | .create_connector = hdmi_create_connector, |
1858 | .get_max_resol = hdmi_get_max_resol, | ||
1859 | .get_edid = hdmi_get_edid, | ||
1860 | .check_mode = hdmi_check_mode, | ||
1861 | .mode_fixup = hdmi_mode_fixup, | 1905 | .mode_fixup = hdmi_mode_fixup, |
1862 | .mode_set = hdmi_mode_set, | 1906 | .mode_set = hdmi_mode_set, |
1863 | .dpms = hdmi_dpms, | 1907 | .dpms = hdmi_dpms, |