ARM: mach-shmobile: mackerel: add HDMI video support
[~shefty/rdma-dev.git] / arch / arm / mach-shmobile / board-mackerel.c
index 42f7c0d67b531836f6913b24cbae322b46c91823..c7d378b57cfce61e5580c2ab0de39a26107e88a2 100644 (file)
@@ -21,6 +21,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
+#include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -43,6 +44,7 @@
 #include <linux/tca6416_keypad.h>
 #include <linux/usb/r8a66597.h>
 
+#include <video/sh_mobile_hdmi.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
 #include <media/soc_camera.h>
@@ -332,6 +334,127 @@ static struct platform_device lcdc_device = {
        },
 };
 
+/* HDMI */
+static struct sh_mobile_lcdc_info hdmi_lcdc_info = {
+       .clock_source = LCDC_CLK_EXTERNAL,
+       .ch[0] = {
+               .chan = LCDC_CHAN_MAINLCD,
+               .bpp = 16,
+               .interface_type = RGB24,
+               .clock_divider = 1,
+               .flags = LCDC_FLAGS_DWPOL,
+       }
+};
+
+static struct resource hdmi_lcdc_resources[] = {
+       [0] = {
+               .name   = "LCDC1",
+               .start  = 0xfe944000,
+               .end    = 0xfe947fff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start  = intcs_evt2irq(0x1780),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device hdmi_lcdc_device = {
+       .name           = "sh_mobile_lcdc_fb",
+       .num_resources  = ARRAY_SIZE(hdmi_lcdc_resources),
+       .resource       = hdmi_lcdc_resources,
+       .id             = 1,
+       .dev    = {
+               .platform_data  = &hdmi_lcdc_info,
+               .coherent_dma_mask = ~0,
+       },
+};
+
+static struct sh_mobile_hdmi_info hdmi_info = {
+       .lcd_chan       = &hdmi_lcdc_info.ch[0],
+       .lcd_dev        = &hdmi_lcdc_device.dev,
+};
+
+static struct resource hdmi_resources[] = {
+       [0] = {
+               .name   = "HDMI",
+               .start  = 0xe6be0000,
+               .end    = 0xe6be00ff,
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] = {
+               /* There's also an HDMI interrupt on INTCS @ 0x18e0 */
+               .start  = evt2irq(0x17e0),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device hdmi_device = {
+       .name           = "sh-mobile-hdmi",
+       .num_resources  = ARRAY_SIZE(hdmi_resources),
+       .resource       = hdmi_resources,
+       .id             = -1,
+       .dev    = {
+               .platform_data  = &hdmi_info,
+       },
+};
+
+static int __init hdmi_init_pm_clock(void)
+{
+       struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick");
+       int ret;
+       long rate;
+
+       if (IS_ERR(hdmi_ick)) {
+               ret = PTR_ERR(hdmi_ick);
+               pr_err("Cannot get HDMI ICK: %d\n", ret);
+               goto out;
+       }
+
+       ret = clk_set_parent(&sh7372_pllc2_clk, &sh7372_dv_clki_div2_clk);
+       if (ret < 0) {
+               pr_err("Cannot set PLLC2 parent: %d, %d users\n",
+                      ret, sh7372_pllc2_clk.usecount);
+               goto out;
+       }
+
+       pr_debug("PLLC2 initial frequency %lu\n",
+                clk_get_rate(&sh7372_pllc2_clk));
+
+       rate = clk_round_rate(&sh7372_pllc2_clk, 594000000);
+       if (rate < 0) {
+               pr_err("Cannot get suitable rate: %ld\n", rate);
+               ret = rate;
+               goto out;
+       }
+
+       ret = clk_set_rate(&sh7372_pllc2_clk, rate);
+       if (ret < 0) {
+               pr_err("Cannot set rate %ld: %d\n", rate, ret);
+               goto out;
+       }
+
+       ret = clk_enable(&sh7372_pllc2_clk);
+       if (ret < 0) {
+               pr_err("Cannot enable pllc2 clock\n");
+               goto out;
+       }
+
+       pr_debug("PLLC2 set frequency %lu\n", rate);
+
+       ret = clk_set_parent(hdmi_ick, &sh7372_pllc2_clk);
+       if (ret < 0) {
+               pr_err("Cannot set HDMI parent: %d\n", ret);
+               goto out;
+       }
+
+out:
+       if (!IS_ERR(hdmi_ick))
+               clk_put(hdmi_ick);
+       return ret;
+}
+device_initcall(hdmi_init_pm_clock);
+
 /* USB1 (Host) */
 static void usb1_host_port_power(int port, int power)
 {
@@ -718,6 +841,8 @@ static struct platform_device *mackerel_devices[] __initdata = {
        &sh_mmcif_device,
        &ceu_device,
        &mackerel_camera,
+       &hdmi_lcdc_device,
+       &hdmi_device,
 };
 
 /* Keypad Initialization */
@@ -793,8 +918,11 @@ static void __init mackerel_map_io(void)
 
 #define GPIO_PORT9CR   0xE6051009
 #define GPIO_PORT10CR  0xE605100A
+#define SRCR4          0xe61580bc
 static void __init mackerel_init(void)
 {
+       u32 srcr4;
+
        sh7372_pinmux_init();
 
        /* enable SCIFA0 */
@@ -935,6 +1063,16 @@ static void __init mackerel_init(void)
        gpio_request(GPIO_FN_VIO_D1, NULL);
        gpio_request(GPIO_FN_VIO_D0, NULL);
 
+       /* HDMI */
+       gpio_request(GPIO_FN_HDMI_HPD, NULL);
+       gpio_request(GPIO_FN_HDMI_CEC, NULL);
+
+       /* Reset HDMI, must be held at least one EXTALR (32768Hz) period */
+       srcr4 = __raw_readl(SRCR4);
+       __raw_writel(srcr4 | (1 << 13), SRCR4);
+       udelay(50);
+       __raw_writel(srcr4 & ~(1 << 13), SRCR4);
+
        i2c_register_board_info(0, i2c0_devices,
                                ARRAY_SIZE(i2c0_devices));
        i2c_register_board_info(1, i2c1_devices,
@@ -949,6 +1087,9 @@ static void __init mackerel_timer_init(void)
 {
        sh7372_clock_init();
        shmobile_timer.init();
+
+       /* External clock source */
+       clk_set_rate(&sh7372_dv_clki_clk, 27000000);
 }
 
 static struct sys_timer mackerel_timer = {