|
@@ -1412,3 +1412,128 @@ at32_add_device_mci(unsigned int id, struct mci_platform_data *data)
|
|
|
case 0:
|
|
|
/* Slot is unused */
|
|
|
break;
|
|
|
+ default:
|
|
|
+ if (!data->slot[0].bus_width)
|
|
|
+ goto fail_free;
|
|
|
+
|
|
|
+ data->slot[1].bus_width = 0;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ atmel_mci0_pclk.dev = &pdev->dev;
|
|
|
+
|
|
|
+ platform_device_add(pdev);
|
|
|
+ return pdev;
|
|
|
+
|
|
|
+fail_free:
|
|
|
+ kfree(slave);
|
|
|
+fail:
|
|
|
+ data->dma_slave = NULL;
|
|
|
+ platform_device_put(pdev);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/* --------------------------------------------------------------------
|
|
|
+ * LCDC
|
|
|
+ * -------------------------------------------------------------------- */
|
|
|
+#if defined(CONFIG_CPU_AT32AP7000) || defined(CONFIG_CPU_AT32AP7002)
|
|
|
+static struct atmel_lcdfb_info atmel_lcdfb0_data;
|
|
|
+static struct resource atmel_lcdfb0_resource[] = {
|
|
|
+ {
|
|
|
+ .start = 0xff000000,
|
|
|
+ .end = 0xff000fff,
|
|
|
+ .flags = IORESOURCE_MEM,
|
|
|
+ },
|
|
|
+ IRQ(1),
|
|
|
+ {
|
|
|
+ /* Placeholder for pre-allocated fb memory */
|
|
|
+ .start = 0x00000000,
|
|
|
+ .end = 0x00000000,
|
|
|
+ .flags = 0,
|
|
|
+ },
|
|
|
+};
|
|
|
+DEFINE_DEV_DATA(atmel_lcdfb, 0);
|
|
|
+DEV_CLK(hck1, atmel_lcdfb0, hsb, 7);
|
|
|
+static struct clk atmel_lcdfb0_pixclk = {
|
|
|
+ .name = "lcdc_clk",
|
|
|
+ .dev = &atmel_lcdfb0_device.dev,
|
|
|
+ .mode = genclk_mode,
|
|
|
+ .get_rate = genclk_get_rate,
|
|
|
+ .set_rate = genclk_set_rate,
|
|
|
+ .set_parent = genclk_set_parent,
|
|
|
+ .index = 7,
|
|
|
+};
|
|
|
+
|
|
|
+struct platform_device *__init
|
|
|
+at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
|
|
|
+ unsigned long fbmem_start, unsigned long fbmem_len,
|
|
|
+ u64 pin_mask)
|
|
|
+{
|
|
|
+ struct platform_device *pdev;
|
|
|
+ struct atmel_lcdfb_info *info;
|
|
|
+ struct fb_monspecs *monspecs;
|
|
|
+ struct fb_videomode *modedb;
|
|
|
+ unsigned int modedb_size;
|
|
|
+ u32 portc_mask, portd_mask, porte_mask;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Do a deep copy of the fb data, monspecs and modedb. Make
|
|
|
+ * sure all allocations are done before setting up the
|
|
|
+ * portmux.
|
|
|
+ */
|
|
|
+ monspecs = kmemdup(data->default_monspecs,
|
|
|
+ sizeof(struct fb_monspecs), GFP_KERNEL);
|
|
|
+ if (!monspecs)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ modedb_size = sizeof(struct fb_videomode) * monspecs->modedb_len;
|
|
|
+ modedb = kmemdup(monspecs->modedb, modedb_size, GFP_KERNEL);
|
|
|
+ if (!modedb)
|
|
|
+ goto err_dup_modedb;
|
|
|
+ monspecs->modedb = modedb;
|
|
|
+
|
|
|
+ switch (id) {
|
|
|
+ case 0:
|
|
|
+ pdev = &atmel_lcdfb0_device;
|
|
|
+
|
|
|
+ if (pin_mask == 0ULL)
|
|
|
+ /* Default to "full" lcdc control signals and 24bit */
|
|
|
+ pin_mask = ATMEL_LCDC_PRI_24BIT | ATMEL_LCDC_PRI_CONTROL;
|
|
|
+
|
|
|
+ /* LCDC on port C */
|
|
|
+ portc_mask = pin_mask & 0xfff80000;
|
|
|
+ select_peripheral(PIOC, portc_mask, PERIPH_A, 0);
|
|
|
+
|
|
|
+ /* LCDC on port D */
|
|
|
+ portd_mask = pin_mask & 0x0003ffff;
|
|
|
+ select_peripheral(PIOD, portd_mask, PERIPH_A, 0);
|
|
|
+
|
|
|
+ /* LCDC on port E */
|
|
|
+ porte_mask = (pin_mask >> 32) & 0x0007ffff;
|
|
|
+ select_peripheral(PIOE, porte_mask, PERIPH_B, 0);
|
|
|
+
|
|
|
+ clk_set_parent(&atmel_lcdfb0_pixclk, &pll0);
|
|
|
+ clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0));
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ goto err_invalid_id;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fbmem_len) {
|
|
|
+ pdev->resource[2].start = fbmem_start;
|
|
|
+ pdev->resource[2].end = fbmem_start + fbmem_len - 1;
|
|
|
+ pdev->resource[2].flags = IORESOURCE_MEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ info = pdev->dev.platform_data;
|
|
|
+ memcpy(info, data, sizeof(struct atmel_lcdfb_info));
|
|
|
+ info->default_monspecs = monspecs;
|
|
|
+
|
|
|
+ platform_device_register(pdev);
|
|
|
+ return pdev;
|
|
|
+
|
|
|
+err_invalid_id:
|
|
|
+ kfree(modedb);
|
|
|
+err_dup_modedb:
|
|
|
+ kfree(monspecs);
|