// SPDX-License-Identifier: GPL-2.0 static int mcde_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct drm_device *drm; struct mcde *mcde; struct component_match *match = NULL; struct resource *res; u32 pid; u32 val; int irq; int ret; int i; mcde = kzalloc(sizeof(*mcde), GFP_KERNEL); if (!mcde) return -ENOMEM; mcde->dev = dev; ret = drm_dev_init(&mcde->drm, &mcde_drm_driver, dev); if (ret) { kfree(mcde); return ret; } drm = &mcde->drm; drm->dev_private = mcde; platform_set_drvdata(pdev, drm); /* Enable continuous updates: this is what Linux' framebuffer expects */ mcde->oneshot_mode = false; drm->dev_private = mcde; /* First obtain and turn on the main power */ mcde->epod = devm_regulator_get(dev, "epod"); if (IS_ERR(mcde->epod)) { ret = PTR_ERR(mcde->epod); dev_err(dev, "can't get EPOD regulator\n"); goto dev_unref; } ret = regulator_enable(mcde->epod); if (ret) { dev_err(dev, "can't enable EPOD regulator\n"); goto dev_unref; } mcde->vana = devm_regulator_get(dev, "vana"); if (IS_ERR(mcde->vana)) { ret = PTR_ERR(mcde->vana); dev_err(dev, "can't get VANA regulator\n"); goto regulator_epod_off; } ret = regulator_enable(mcde->vana); if (ret) { dev_err(dev, "can't enable VANA regulator\n"); goto regulator_epod_off; } /* * The vendor code uses ESRAM (onchip RAM) and need to activate * the v-esram34 regulator, but we don't use that yet */ /* Clock the silicon so we can access the registers */ mcde->mcde_clk = devm_clk_get(dev, "mcde"); if (IS_ERR(mcde->mcde_clk)) { dev_err(dev, "unable to get MCDE main clock\n"); ret = PTR_ERR(mcde->mcde_clk); goto regulator_off; } ret = clk_prepare_enable(mcde->mcde_clk); if (ret) { dev_err(dev, "failed to enable MCDE main clock\n"); goto regulator_off; } dev_info(dev, "MCDE clk rate %lu Hz\n", clk_get_rate(mcde->mcde_clk)); mcde->lcd_clk = devm_clk_get(dev, "lcd"); if (IS_ERR(mcde->lcd_clk)) { dev_err(dev, "unable to get LCD clock\n"); ret = PTR_ERR(mcde->lcd_clk); goto clk_disable; } mcde->hdmi_clk = devm_clk_get(dev, "hdmi"); if (IS_ERR(mcde->hdmi_clk)) { dev_err(dev, "unable to get HDMI clock\n"); ret = PTR_ERR(mcde->hdmi_clk); goto clk_disable; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mcde->regs = devm_ioremap_resource(dev, res); if (IS_ERR(mcde->regs)) { dev_err(dev, "no MCDE regs\n"); ret = -EINVAL; goto clk_disable; } irq = platform_get_irq(pdev, 0); if (!irq) { ret = -EINVAL; goto clk_disable; } ret = devm_request_irq(dev, irq, mcde_irq, 0, "mcde", mcde); if (ret) { dev_err(dev, "failed to request irq %d\n", ret); goto clk_disable; } /* * Check hardware revision, we only support U8500v2 version * as this was the only version used for mass market deployment, * but surely you can add more versions if you have them and * need them. */ pid = readl(mcde->regs + MCDE_PID); dev_info(dev, "found MCDE HW revision %d.%d (dev %d, metal fix %d)\n", (pid & MCDE_PID_MAJOR_VERSION_MASK) >> MCDE_PID_MAJOR_VERSION_SHIFT, (pid & MCDE_PID_MINOR_VERSION_MASK) >> MCDE_PID_MINOR_VERSION_SHIFT, (pid & MCDE_PID_DEVELOPMENT_VERSION_MASK) >> MCDE_PID_DEVELOPMENT_VERSION_SHIFT, (pid & MCDE_PID_METALFIX_VERSION_MASK) >> MCDE_PID_METALFIX_VERSION_SHIFT); if (pid != 0x03000800) { dev_err(dev, "unsupported hardware revision\n"); ret = -ENODEV; goto clk_disable; } /* Set up the main control, watermark level at 7 */ val = 7 << MCDE_CONF0_IFIFOCTRLWTRMRKLVL_SHIFT; /* 24 bits DPI: connect LSB Ch B to D[0:7] */ val |= 3 << MCDE_CONF0_OUTMUX0_SHIFT; /* TV out: connect LSB Ch B to D[8:15] */ val |= 3 << MCDE_CONF0_OUTMUX1_SHIFT; /* Don't care about this muxing */ val |= 0 << MCDE_CONF0_OUTMUX2_SHIFT; /* 24 bits DPI: connect MID Ch B to D[24:31] */ val |= 4 << MCDE_CONF0_OUTMUX3_SHIFT; /* 5: 24 bits DPI: connect MSB Ch B to D[32:39] */ val |= 5 << MCDE_CONF0_OUTMUX4_SHIFT; /* Syncmux bits zero: DPI channel A and B on output pins A and B resp */ writel(val, mcde->regs + MCDE_CONF0); /* Enable automatic clock gating */ val = readl(mcde->regs + MCDE_CR); val |= MCDE_CR_MCDEEN | MCDE_CR_AUTOCLKG_EN; writel(val, mcde->regs + MCDE_CR); /* Clear any pending interrupts */ mcde_display_disable_irqs(mcde); writel(0, mcde->regs + MCDE_IMSCERR); writel(0xFFFFFFFF, mcde->regs + MCDE_RISERR); /* Spawn child devices for the DSI ports */ devm_of_platform_populate(dev); /* Create something that will match the subdrivers when we bind */ for (i = 0; i < ARRAY_SIZE(mcde_component_drivers); i++) { struct device_driver *drv = &mcde_component_drivers[i]->driver; struct device *p = NULL, *d; while ((d = platform_find_device_by_driver(p, drv))) { put_device(p); component_match_add(dev, &match, mcde_compare_dev, d); p = d; } put_device(p); } if (!match) { dev_err(dev, "no matching components\n"); ret = -ENODEV; goto clk_disable; } if (IS_ERR(match)) { dev_err(dev, "could not create component match\n"); ret = PTR_ERR(match); goto clk_disable; } ret = component_master_add_with_match(&pdev->dev, &mcde_drm_comp_ops, match); if (ret) { dev_err(dev, "failed to add component master\n"); goto clk_disable; } return 0; clk_disable: clk_disable_unprepare(mcde->mcde_clk); regulator_off: regulator_disable(mcde->vana); regulator_epod_off: regulator_disable(mcde->epod); dev_unref: drm_dev_put(drm); return ret; }