static int tegra_dc_probe(struct nvhost_device *ndev) { struct tegra_dc *dc; struct clk *clk; struct clk *emc_clk; struct resource *res; struct resource *base_res; struct resource *fb_mem = NULL; int ret = 0; void __iomem *base; int irq; int i; unsigned long emc_clk_rate; if (!ndev->dev.platform_data) { dev_err(&ndev->dev, "no platform data\n"); return -ENOENT; } dc = kzalloc(sizeof(struct tegra_dc), GFP_KERNEL); if (!dc) { dev_err(&ndev->dev, "can't allocate memory for tegra_dc\n"); return -ENOMEM; } irq = nvhost_get_irq_byname(ndev, "irq"); if (irq <= 0) { dev_err(&ndev->dev, "no irq\n"); ret = -ENOENT; goto err_free; } res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "regs"); if (!res) { dev_err(&ndev->dev, "no mem resource\n"); ret = -ENOENT; goto err_free; } base_res = request_mem_region(res->start, resource_size(res), ndev->name); if (!base_res) { dev_err(&ndev->dev, "request_mem_region failed\n"); ret = -EBUSY; goto err_free; } base = ioremap(res->start, resource_size(res)); if (!base) { dev_err(&ndev->dev, "registers can't be mapped\n"); ret = -EBUSY; goto err_release_resource_reg; } fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem"); clk = clk_get(&ndev->dev, NULL); if (IS_ERR_OR_NULL(clk)) { dev_err(&ndev->dev, "can't get clock\n"); ret = -ENOENT; goto err_iounmap_reg; } emc_clk = clk_get(&ndev->dev, "emc"); if (IS_ERR_OR_NULL(emc_clk)) { dev_err(&ndev->dev, "can't get emc clock\n"); ret = -ENOENT; goto err_put_clk; } dc->clk = clk; dc->emc_clk = emc_clk; dc->base_res = base_res; dc->base = base; dc->irq = irq; dc->ndev = ndev; dc->pdata = ndev->dev.platform_data; /* * The emc is a shared clock, it will be set based on * the requirements for each user on the bus. */ emc_clk_rate = dc->pdata->emc_clk_rate; clk_set_rate(emc_clk, emc_clk_rate ? emc_clk_rate : ULONG_MAX); if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) dc->enabled = true; mutex_init(&dc->lock); init_waitqueue_head(&dc->wq); INIT_WORK(&dc->reset_work, tegra_dc_reset_worker); dc->n_windows = DC_N_WINDOWS; for (i = 0; i < dc->n_windows; i++) { dc->windows[i].idx = i; dc->windows[i].dc = dc; } if (request_irq(irq, tegra_dc_irq, IRQF_DISABLED, dev_name(&ndev->dev), dc)) { dev_err(&ndev->dev, "request_irq %d failed\n", irq); ret = -EBUSY; goto err_put_emc_clk; } /* hack to ballence enable_irq calls in _tegra_dc_enable() */ disable_irq(dc->irq); ret = tegra_dc_add(dc, ndev->id); if (ret < 0) { dev_err(&ndev->dev, "can't add dc\n"); goto err_free_irq; } nvhost_set_drvdata(ndev, dc); if (dc->pdata->default_out) tegra_dc_set_out(dc, dc->pdata->default_out); else dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n"); dc->vblank_syncpt = (dc->ndev->id == 0) ? NVSYNCPT_VBLANK0 : NVSYNCPT_VBLANK1; dc->ext = tegra_dc_ext_register(ndev, dc); if (IS_ERR_OR_NULL(dc->ext)) { dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n"); dc->ext = NULL; } if (dc->enabled) _tegra_dc_enable(dc); tegra_dc_dbg_add(dc); dev_info(&ndev->dev, "probed\n"); if (dc->pdata->fb) { if (dc->pdata->fb->bits_per_pixel == -1) { unsigned long fmt; tegra_dc_writel(dc, WINDOW_A_SELECT << dc->pdata->fb->win, DC_CMD_DISPLAY_WINDOW_HEADER); fmt = tegra_dc_readl(dc, DC_WIN_COLOR_DEPTH); dc->pdata->fb->bits_per_pixel = tegra_dc_fmt_bpp(fmt); } dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem); if (IS_ERR_OR_NULL(dc->fb)) dc->fb = NULL; } if (dc->out && dc->out->hotplug_init) dc->out->hotplug_init(); if (dc->out_ops && dc->out_ops->detect) dc->out_ops->detect(dc); else dc->connected = true; return 0; err_free_irq: free_irq(irq, dc); err_put_emc_clk: clk_put(emc_clk); err_put_clk: clk_put(clk); err_iounmap_reg: iounmap(base); if (fb_mem) release_resource(fb_mem); err_release_resource_reg: release_resource(base_res); err_free: kfree(dc); return ret; }
static int tegra_dc_probe(struct nvhost_device *ndev, struct nvhost_device_id *id_table) { struct tegra_dc *dc; struct tegra_dc_mode *mode; struct clk *clk; struct clk *emc_clk; struct resource *res; struct resource *base_res; struct resource *fb_mem = NULL; int ret = 0; void __iomem *base; int irq; int i; if (!ndev->dev.platform_data) { dev_err(&ndev->dev, "no platform data\n"); return -ENOENT; } dc = kzalloc(sizeof(struct tegra_dc), GFP_KERNEL); if (!dc) { dev_err(&ndev->dev, "can't allocate memory for tegra_dc\n"); return -ENOMEM; } irq = nvhost_get_irq_byname(ndev, "irq"); if (irq <= 0) { dev_err(&ndev->dev, "no irq\n"); ret = -ENOENT; goto err_free; } res = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "regs"); if (!res) { dev_err(&ndev->dev, "no mem resource\n"); ret = -ENOENT; goto err_free; } base_res = request_mem_region(res->start, resource_size(res), ndev->name); if (!base_res) { dev_err(&ndev->dev, "request_mem_region failed\n"); ret = -EBUSY; goto err_free; } base = ioremap(res->start, resource_size(res)); if (!base) { dev_err(&ndev->dev, "registers can't be mapped\n"); ret = -EBUSY; goto err_release_resource_reg; } fb_mem = nvhost_get_resource_byname(ndev, IORESOURCE_MEM, "fbmem"); clk = clk_get(&ndev->dev, NULL); if (IS_ERR_OR_NULL(clk)) { dev_err(&ndev->dev, "can't get clock\n"); ret = -ENOENT; goto err_iounmap_reg; } emc_clk = clk_get(&ndev->dev, "emc"); if (IS_ERR_OR_NULL(emc_clk)) { dev_err(&ndev->dev, "can't get emc clock\n"); ret = -ENOENT; goto err_put_clk; } dc->clk = clk; dc->emc_clk = emc_clk; dc->shift_clk_div = 1; /* Initialize one shot work delay, it will be assigned by dsi * according to refresh rate later. */ dc->one_shot_delay_ms = 40; dc->base_res = base_res; dc->base = base; dc->irq = irq; dc->ndev = ndev; dc->pdata = ndev->dev.platform_data; /* * The emc is a shared clock, it will be set based on * the requirements for each user on the bus. */ dc->emc_clk_rate = 0; mutex_init(&dc->lock); mutex_init(&dc->one_shot_lock); init_completion(&dc->frame_end_complete); init_waitqueue_head(&dc->wq); init_waitqueue_head(&dc->timestamp_wq); #ifdef CONFIG_ARCH_TEGRA_2x_SOC INIT_WORK(&dc->reset_work, tegra_dc_reset_worker); #endif INIT_WORK(&dc->vblank_work, tegra_dc_vblank); dc->vblank_ref_count = 0; INIT_DELAYED_WORK(&dc->underflow_work, tegra_dc_underflow_worker); INIT_DELAYED_WORK(&dc->one_shot_work, tegra_dc_one_shot_worker); tegra_dc_init_lut_defaults(&dc->fb_lut); dc->n_windows = DC_N_WINDOWS; for (i = 0; i < dc->n_windows; i++) { struct tegra_dc_win *win = &dc->windows[i]; win->idx = i; win->dc = dc; tegra_dc_init_csc_defaults(&win->csc); tegra_dc_init_lut_defaults(&win->lut); } ret = tegra_dc_set(dc, ndev->id); if (ret < 0) { dev_err(&ndev->dev, "can't add dc\n"); goto err_free_irq; } nvhost_set_drvdata(ndev, dc); #ifdef CONFIG_SWITCH dc->modeset_switch.name = dev_name(&ndev->dev); dc->modeset_switch.state = 0; dc->modeset_switch.print_state = switch_modeset_print_mode; switch_dev_register(&dc->modeset_switch); #endif tegra_dc_feature_register(dc); if (dc->pdata->default_out) tegra_dc_set_out(dc, dc->pdata->default_out); else dev_err(&ndev->dev, "No default output specified. Leaving output disabled.\n"); dc->vblank_syncpt = (dc->ndev->id == 0) ? NVSYNCPT_VBLANK0 : NVSYNCPT_VBLANK1; dc->ext = tegra_dc_ext_register(ndev, dc); if (IS_ERR_OR_NULL(dc->ext)) { dev_warn(&ndev->dev, "Failed to enable Tegra DC extensions.\n"); dc->ext = NULL; } /* interrupt handler must be registered before tegra_fb_register() */ if (request_irq(irq, tegra_dc_irq, 0, dev_name(&ndev->dev), dc)) { dev_err(&ndev->dev, "request_irq %d failed\n", irq); ret = -EBUSY; goto err_put_emc_clk; } disable_dc_irq(irq); mutex_lock(&dc->lock); if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED) dc->enabled = _tegra_dc_enable(dc); mutex_unlock(&dc->lock); tegra_dc_create_debugfs(dc); dev_info(&ndev->dev, "probed\n"); if (dc->pdata->fb) { if (dc->enabled && dc->pdata->fb->bits_per_pixel == -1) { unsigned long fmt; tegra_dc_writel(dc, WINDOW_A_SELECT << dc->pdata->fb->win, DC_CMD_DISPLAY_WINDOW_HEADER); fmt = tegra_dc_readl(dc, DC_WIN_COLOR_DEPTH); dc->pdata->fb->bits_per_pixel = tegra_dc_fmt_bpp(fmt); } mode = tegra_dc_get_override_mode(dc); if (mode) { dc->pdata->fb->xres = mode->h_active; dc->pdata->fb->yres = mode->v_active; } dc->fb = tegra_fb_register(ndev, dc, dc->pdata->fb, fb_mem); if (IS_ERR_OR_NULL(dc->fb)) dc->fb = NULL; } if (dc->out && dc->out->hotplug_init) dc->out->hotplug_init(); if (dc->out_ops && dc->out_ops->detect) dc->out_ops->detect(dc); else dc->connected = true; tegra_dc_create_sysfs(&dc->ndev->dev); return 0; err_free_irq: free_irq(irq, dc); err_put_emc_clk: clk_put(emc_clk); err_put_clk: clk_put(clk); err_iounmap_reg: iounmap(base); if (fb_mem) release_resource(fb_mem); err_release_resource_reg: release_resource(base_res); err_free: kfree(dc); return ret; }