int find_subdev_mipi(struct decon_device *decon) { struct exynos_md *md; if (decon->id && decon->pdata->dsi_mode == DSI_MODE_SINGLE) { decon_err("failed to get subdev of dsim\n"); return -EINVAL; } md = (struct exynos_md *)module_name_to_driver_data(MDEV_MODULE_NAME); if (!md) { decon_err("failed to get mdev device(%d)\n", decon->id); return -ENODEV; } decon->output_sd = md->dsim_sd[decon->id]; decon->out_type = DECON_OUT_DSI; if (IS_ERR_OR_NULL(decon->output_sd)) decon_warn("couldn't find dsim%d subdev\n", decon->id); v4l2_subdev_call(decon->output_sd, core, ioctl, DSIM_IOC_GET_LCD_INFO, NULL); decon->lcd_info = (struct decon_lcd *)v4l2_get_subdev_hostdata(decon->output_sd); if (IS_ERR_OR_NULL(decon->lcd_info)) { decon_err("failed to get lcd information\n"); return -EINVAL; } return 0; }
int decon_ext_register_irq(struct platform_device *pdev, struct decon_device *decon) { struct device *dev = decon->dev; struct resource *res; int ret = 0; irqreturn_t (*irq_handler)(int, void *); if (decon->out_type == DECON_OUT_DSI) irq_handler = decon_ext_dsi_irq_handler; else irq_handler = decon_ext_wb_irq_handler; /* Get IRQ resource and register IRQ handler. */ /* 0: FIFO irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ret = devm_request_irq(dev, res->start, irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 1: frame irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ret = devm_request_irq(dev, res->start, irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 2: i80 irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); ret = devm_request_irq(dev, res->start, irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 3: wb frame done irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 3); ret = devm_request_irq(dev, res->start, irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 4: external irq for te */ if (decon->pdata->dsi_mode == DSI_MODE_DUAL_DISPLAY) { ret = decon_ext_config_eint_for_te(pdev, decon); if (ret) { decon_err("failed to config external irq\n"); return ret; } } return ret; }
int decon_get_hdmi_config(struct decon_device *decon, struct exynos_hdmi_data *hdmi_data) { struct v4l2_control ctrl; int ret = 0; ctrl.id = 0; ctrl.value = 0; decon_dbg("state : %d\n", hdmi_data->state); switch (hdmi_data->state) { case EXYNOS_HDMI_STATE_PRESET: ret = v4l2_subdev_call(decon->output_sd, video, g_dv_timings, &hdmi_data->timings); if (ret) decon_err("failed to get current timings\n"); else ret = find_subdev_hdmi(decon); decon_dbg("%dx%d@%s %lldHz %s(%#x)\n", hdmi_data->timings.bt.width, hdmi_data->timings.bt.height, hdmi_data->timings.bt.interlaced ? "I" : "P", hdmi_data->timings.bt.pixelclock, hdmi_data->timings.type ? "S3D" : "2D", hdmi_data->timings.type); break; case EXYNOS_HDMI_STATE_ENUM_PRESET: ret = v4l2_subdev_call(decon->output_sd, video, enum_dv_timings, &hdmi_data->etimings); if (ret) decon_err("failed to enumerate timings\n"); break; case EXYNOS_HDMI_STATE_CEC_ADDR: ctrl.id = V4L2_CID_TV_SOURCE_PHY_ADDR; ret = v4l2_subdev_call(decon->output_sd, core, g_ctrl, &ctrl); if (ret) decon_err("failed to get physical address for CEC\n"); hdmi_data->cec_addr = ctrl.value; decon_dbg("get physical address for CEC: %#x\n", hdmi_data->cec_addr); break; case EXYNOS_HDMI_STATE_AUDIO: ctrl.id = V4L2_CID_TV_MAX_AUDIO_CHANNELS; ret = v4l2_subdev_call(decon->output_sd, core, g_ctrl, &ctrl); if (ret) decon_err("failed to get hdmi audio information\n"); hdmi_data->audio_info = ctrl.value; break; default: decon_warn("unrecongnized state %u", hdmi_data->state); ret = -EINVAL; break; } return ret; }
dma_addr_t decon_map_sec_dma_buf(struct dma_buf *dbuf, int plane) { struct decon_device *decon = get_decon_drvdata(0); /* 0: decon Int ID */ if (!dbuf || (plane >= MAX_BUF_PLANE_CNT) || (plane < 0)) return -EINVAL; dma.ion_handle = NULL; dma.fence = NULL; dma.dma_buf = dbuf; dma.attachment = dma_buf_attach(dbuf, decon->dev); if (IS_ERR(dma.attachment)) { decon_err("dma_buf_attach() failed: %ld\n", PTR_ERR(dma.attachment)); goto err_buf_map_attach; } dma.sg_table = dma_buf_map_attachment(dma.attachment, DMA_TO_DEVICE); if (IS_ERR(dma.sg_table)) { decon_err("dma_buf_map_attachment() failed: %ld\n", PTR_ERR(dma.sg_table)); goto err_buf_map_attachment; } dma.dma_addr = ion_iovmm_map(dma.attachment, 0, dma.dma_buf->size, DMA_TO_DEVICE, plane); if (IS_ERR_VALUE(dma.dma_addr)) { decon_err("iovmm_map() failed: %pa\n", &dma.dma_addr); goto err_iovmm_map; } exynos_ion_sync_dmabuf_for_device(decon->dev, dma.dma_buf, dma.dma_buf->size, DMA_TO_DEVICE); return dma.dma_addr; err_iovmm_map: dma_buf_unmap_attachment(dma.attachment, dma.sg_table, DMA_TO_DEVICE); err_buf_map_attachment: dma_buf_detach(dma.dma_buf, dma.attachment); err_buf_map_attach: return 0; }
irqreturn_t decon_t_irq_handler(int irq, void *dev_data) { struct decon_device *decon = dev_data; u32 irq_sts_reg; spin_lock(&decon->slock); if ((decon->state == DECON_STATE_OFF) || (decon->state == DECON_STATE_LPD)) { goto irq_end; } irq_sts_reg = decon_reg_get_interrupt_and_clear(decon->id); if (irq_sts_reg & INTERRUPT_FIFO_LEVEL_INT_EN) { DISP_SS_EVENT_LOG(DISP_EVT_UNDERRUN, &decon->sd, ktime_set(0, 0)); decon_err("DECON_T FIFO underrun\n"); } if (irq_sts_reg & INTERRUPT_FRAME_DONE_INT_EN) { decon_lpd_trig_reset(decon); DISP_SS_EVENT_LOG(DISP_EVT_DECON_FRAMEDONE, &decon->sd, ktime_set(0, 0)); decon_dbg("%s Frame Done is occured. timeline:%d, %d\n", __func__, decon->timeline->value, decon->timeline_max); } if (irq_sts_reg & INTERRUPT_RESOURCE_CONFLICT_INT_EN) DISP_SS_EVENT_LOG(DISP_EVT_RSC_CONFLICT, &decon->sd, ktime_set(0, 0)); irq_end: spin_unlock(&decon->slock); return IRQ_HANDLED; }
int create_link_mipi(struct decon_device *decon) { int i, ret = 0; int n_pad = decon->n_sink_pad + decon->n_src_pad; int flags = 0; char err[80]; struct exynos_md *md = decon->mdev; if (IS_ERR_OR_NULL(md->dsim_sd[decon->id])) { decon_err("failed to get subdev of dsim%d\n", decon->id); return -EINVAL; } flags = MEDIA_LNK_FL_ENABLED; for (i = decon->n_sink_pad; i < n_pad ; i++) { ret = media_entity_create_link(&decon->sd.entity, i, &md->dsim_sd[decon->id]->entity, 0, flags); if (ret) { snprintf(err, sizeof(err), "%s --> %s", decon->sd.entity.name, decon->output_sd->entity.name); return ret; } decon_info("%s[%d] --> [0]%s link is created successfully\n", decon->sd.entity.name, i, decon->output_sd->entity.name); } return ret; }
int decon_t_set_lcd_info(struct decon_device *decon) { struct decon_lcd *lcd_info; if (decon->lcd_info != NULL) return 0; lcd_info = kzalloc(sizeof(struct decon_lcd), GFP_KERNEL); if (!lcd_info) { decon_err("could not allocate decon_lcd for wb\n"); return -ENOMEM; } decon->lcd_info = lcd_info; decon->lcd_info->width = 1920; decon->lcd_info->height = 1080; decon->lcd_info->xres = 1920; decon->lcd_info->yres = 1080; decon->lcd_info->vfp = 2; decon->lcd_info->vbp = 20; decon->lcd_info->hfp = 20; decon->lcd_info->hbp = 20; decon->lcd_info->vsa = 2; decon->lcd_info->hsa = 20; decon->lcd_info->fps = 60; decon->pdata->out_type = DECON_OUT_WB; decon_info("decon_%d output size for writeback %dx%d\n", decon->id, decon->lcd_info->width, decon->lcd_info->height); return 0; }
int decon_t_register_irq(struct platform_device *pdev, struct decon_device *decon) { struct device *dev = decon->dev; struct resource *res; int ret = 0; /* Get IRQ resource and register IRQ handler. */ /* 0: FIFO irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ret = devm_request_irq(dev, res->start, decon_t_irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 1: VStatus irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ret = devm_request_irq(dev, res->start, decon_t_irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 2: FrameDone irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 2); ret = devm_request_irq(dev, res->start, decon_t_irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } /* 3: Extra Interrupts: Resource Conflict irq */ res = platform_get_resource(pdev, IORESOURCE_IRQ, 3); ret = devm_request_irq(dev, res->start, decon_t_irq_handler, 0, pdev->name, decon); if (ret) { decon_err("failed to install irq\n"); return ret; } return ret; }
int decon_set_hdmi_config(struct decon_device *decon, struct exynos_hdmi_data *hdmi_data) { struct v4l2_control ctrl; int ret = 0; decon_dbg("state : %d\n", hdmi_data->state); switch (hdmi_data->state) { case EXYNOS_HDMI_STATE_PRESET: ret = v4l2_subdev_call(decon->output_sd, video, s_dv_timings, &hdmi_data->timings); if (ret) decon_err("failed to set timings newly\n"); else ret = find_subdev_hdmi(decon); decon_dbg("%dx%d@%s %lldHz %s(%#x)\n", hdmi_data->timings.bt.width, hdmi_data->timings.bt.height, hdmi_data->timings.bt.interlaced ? "I" : "P", hdmi_data->timings.bt.pixelclock, hdmi_data->timings.type ? "S3D" : "2D", hdmi_data->timings.type); break; case EXYNOS_HDMI_STATE_HDCP: ctrl.id = V4L2_CID_TV_HDCP_ENABLE; ctrl.value = hdmi_data->hdcp; ret = v4l2_subdev_call(decon->output_sd, core, s_ctrl, &ctrl); if (ret) decon_err("failed to enable HDCP\n"); decon_dbg("HDCP %s\n", ctrl.value ? "enabled" : "disabled"); break; case EXYNOS_HDMI_STATE_AUDIO: ctrl.id = V4L2_CID_TV_SET_NUM_CHANNELS; ctrl.value = hdmi_data->audio_info; ret = v4l2_subdev_call(decon->output_sd, core, s_ctrl, &ctrl); if (ret) decon_err("failed to set hdmi audio information\n"); break; default: decon_warn("unrecongnized state %u", hdmi_data->state); ret = -EINVAL; break; } return ret; }
int decon_ext_get_clocks(struct decon_device *decon) { decon->res.pclk = clk_get(decon->dev, "pclk_decon1"); if (IS_ERR_OR_NULL(decon->res.pclk)) { decon_err("failed to get pclk_decon1\n"); return -ENODEV; } decon->res.aclk = clk_get(decon->dev, "aclk_decon1"); if (IS_ERR_OR_NULL(decon->res.aclk)) { decon_err("failed to get aclk_decon1\n"); return -ENODEV; } decon->res.eclk = clk_get(decon->dev, "decon1_eclk"); if (IS_ERR_OR_NULL(decon->res.eclk)) { decon_err("failed to get decon1_eclk\n"); return -ENODEV; } decon->res.vclk = clk_get(decon->dev, "decon1_vclk"); if (IS_ERR_OR_NULL(decon->res.vclk)) { decon_err("failed to get decon1_vclk\n"); return -ENODEV; } decon->res.dsd = clk_get(decon->dev, "sclk_dsd"); if (IS_ERR_OR_NULL(decon->res.dsd)) { decon_err("failed to get sclk_dsd\n"); return -ENODEV; } decon->res.lh_disp1 = clk_get(decon->dev, "aclk_lh_disp1"); if (IS_ERR_OR_NULL(decon->res.lh_disp1)) { decon_err("failed to get aclk_lh_disp1\n"); return -ENODEV; } decon->res.aclk_disp = clk_get(decon->dev, "aclk_disp"); if (IS_ERR_OR_NULL(decon->res.aclk_disp)) { decon_err("failed to get aclk_disp\n"); return -ENODEV; } decon->res.pclk_disp = clk_get(decon->dev, "pclk_disp"); if (IS_ERR_OR_NULL(decon->res.pclk_disp)) { decon_err("failed to get pclk_disp\n"); return -ENODEV; } return 0; }
int decon_t_get_clocks(struct decon_device *decon) { decon->res.pclk = clk_get(decon->dev, "decon_pclk"); if (IS_ERR_OR_NULL(decon->res.pclk)) { decon_err("failed to get decon_pclk\n"); return -ENODEV; } decon->res.eclk = clk_get(decon->dev, "eclk_user"); if (IS_ERR_OR_NULL(decon->res.eclk)) { decon_err("failed to get eclk_user\n"); return -ENODEV; } decon->res.eclk_leaf = clk_get(decon->dev, "eclk_leaf"); if (IS_ERR_OR_NULL(decon->res.eclk_leaf)) { decon_err("failed to get eclk_leaf\n"); return -ENODEV; } return 0; }
int decon_clk_set_parent(struct device *dev, const char *child, const char *parent) { struct clk *p; struct clk *c; p = clk_get(dev, parent); if (IS_ERR_OR_NULL(p)) { decon_err("%s: couldn't get clock : %s\n", __func__, parent); return -ENODEV; } c = clk_get(dev, child); if (IS_ERR_OR_NULL(c)) { decon_err("%s: couldn't get clock : %s\n", __func__, child); return -ENODEV; } clk_set_parent(c, p); clk_put(p); clk_put(c); return 0; }
int decon_clk_set_rate(struct device *dev, const char *conid, unsigned int rate) { struct clk *target; target = clk_get(dev, conid); if (IS_ERR_OR_NULL(target)) { decon_err("%s: couldn't get clock : %s\n", __func__, conid); return -ENODEV; } clk_set_rate(target, rate); clk_put(target); return 0; }
/* wait until shadow update is finished */ int decon_reg_wait_for_update_timeout(unsigned long timeout) { unsigned long delay_time = 100; unsigned long cnt = timeout / delay_time; while ((decon_read(DECON_UPDATE) & DECON_UPDATE_STANDALONE_F) && cnt--) udelay(delay_time); if (!cnt) { decon_err("timeout of updating decon registers\n"); return -EBUSY; } return 0; }
int find_subdev_hdmi(struct decon_device *decon) { struct v4l2_subdev *output_sd; output_sd = (struct v4l2_subdev *)module_name_to_driver_data("s5p-hdmi"); if (!output_sd) { decon_err("failed to get hdmi device\n"); return -ENODEV; } decon->output_sd = output_sd; decon->out_type = DECON_OUT_HDMI; decon_info("%s entity get successfully\n", output_sd->name); return 0; }
unsigned long decon_clk_get_rate(struct device *dev, const char *clkid) { struct clk *target; unsigned long rate; target = clk_get(dev, clkid); if (IS_ERR_OR_NULL(target)) { decon_err("%s: couldn't get clock : %s\n", __func__, clkid); return -ENODEV; } rate = clk_get_rate(target); clk_put(target); return rate; }
/******************* CAL raw functions implementation *************************/ int decon_reg_reset(void) { int tries; decon_write(VIDCON0, VIDCON0_SWRESET); for (tries = 2000; tries; --tries) { if (~decon_read(VIDCON0) & VIDCON0_SWRESET) break; udelay(10); } if (!tries) { decon_err("failed to reset Decon\n"); return -EBUSY; } return 0; }
int decon_int_get_clocks(struct decon_device *decon) { decon->res.pclk = clk_get(decon->dev, "pclk_decon0"); if (IS_ERR_OR_NULL(decon->res.pclk)) { decon_err("failed to get pclk_decon0\n"); return -ENODEV; } decon->res.aclk = clk_get(decon->dev, "aclk_decon0"); if (IS_ERR_OR_NULL(decon->res.aclk)) { decon_err("failed to get aclk_decon0\n"); return -ENODEV; } decon->res.eclk = clk_get(decon->dev, "decon0_eclk"); if (IS_ERR_OR_NULL(decon->res.eclk)) { decon_err("failed to get decon0_eclk\n"); return -ENODEV; } decon->res.vclk = clk_get(decon->dev, "decon0_vclk"); if (IS_ERR_OR_NULL(decon->res.vclk)) { decon_err("failed to get decon0_vclk\n"); return -ENODEV; } decon->res.aclk_disp = clk_get(decon->dev, "aclk_disp"); if (IS_ERR_OR_NULL(decon->res.aclk_disp)) { decon_err("failed to get aclk_disp\n"); return -ENODEV; } decon->res.mif_pll = clk_get(decon->dev, "mif_pll"); if (IS_ERR_OR_NULL(decon->res.mif_pll)) { decon_err("failed to get mif_pll\n"); return -ENODEV; } decon->res.aclk_disp_200 = clk_get(decon->dev, "aclk_disp_200"); if (IS_ERR_OR_NULL(decon->res.aclk_disp_200)) { decon_err("failed to get aclk_disp_200\n"); return -ENODEV; } return 0; }
int decon_ext_config_eint_for_te(struct platform_device *pdev, struct decon_device *decon) { struct device *dev = decon->dev; int gpio; int ret = 0; /* Get IRQ resource and register IRQ handler. */ gpio = of_get_gpio(dev->of_node, 0); if (gpio < 0) { decon_err("failed to get proper gpio number\n"); return -EINVAL; } gpio = gpio_to_irq(gpio); ret = devm_request_irq(dev, gpio, decon_ext_isr_for_eint, IRQF_TRIGGER_RISING, pdev->name, decon); return ret; }
int decon_reg_wait_stop_status_timeout(unsigned long timeout) { unsigned long delay_time = 10; unsigned long cnt = timeout / delay_time; u32 status; do { status = decon_reg_get_stop_status(); cnt--; udelay(delay_time); } while (status && cnt); if (!cnt) { decon_err("wait timeout decon stop status(%u)\n", status); return -EBUSY; } return 0; }
irqreturn_t decon_ext_dsi_irq_handler(int irq, void *dev_data) { struct decon_device *decon = dev_data; ktime_t timestamp = ktime_get(); u32 irq_sts_reg; u32 wb_irq_sts_reg; spin_lock(&decon->slock); irq_sts_reg = decon_read(decon->id, VIDINTCON1); wb_irq_sts_reg = decon_read(decon->id, VIDINTCON3); if (irq_sts_reg & VIDINTCON1_INT_FRAME) { /* VSYNC interrupt, accept it */ decon_write_mask(decon->id, VIDINTCON1, ~0, VIDINTCON1_INT_FRAME); decon->vsync_info.timestamp = timestamp; wake_up_interruptible_all(&decon->vsync_info.wait); } if (irq_sts_reg & VIDINTCON1_INT_FIFO) { decon_err("DECON-ext FIFO underrun\n"); decon_write_mask(decon->id, VIDINTCON1, ~0, VIDINTCON1_INT_FIFO); } if (irq_sts_reg & VIDINTCON1_INT_I80) { decon_write_mask(decon->id, VIDINTCON1, ~0, VIDINTCON1_INT_I80); wake_up_interruptible_all(&decon->wait_frmdone); } #if 0 if (wb_irq_sts_reg & VIDINTCON3_WB_FRAME_DONE) { decon_dbg("write-back frame done\n"); DISP_SS_EVENT_LOG(DISP_EVT_WB_FRAME_DONE, &decon->sd, ktime_set(0, 0)); decon_write_mask(decon->id, VIDINTCON3, ~0, VIDINTCON3_WB_FRAME_DONE); atomic_set(&decon->wb_done, STATE_DONE); wake_up_interruptible_all(&decon->wait_frmdone); decon_reg_per_frame_off(decon->id); decon_reg_update_standalone(decon->id); decon_reg_wb_swtrigger(decon->id); decon_reg_wait_stop_status_timeout(decon->id, 20 * 1000); } #endif spin_unlock(&decon->slock); return IRQ_HANDLED; }
void decon_ext_set_clocks(struct decon_device *decon) { struct device *dev = decon->dev; if (decon->out_type == DECON_OUT_HDMI) { decon_clk_set_parent(dev, "m_decon1_eclk", "um_decon1_eclk"); decon_clk_set_rate(dev, "d_decon1_eclk", 200 * MHZ); decon_clk_set_parent(dev, "m_decon1_vclk", "hdmi_pixel"); } else if (decon->out_type == DECON_OUT_WB) { decon_clk_set_parent(dev, "m_decon1_eclk", "um_decon1_eclk"); decon_clk_set_rate(dev, "d_decon1_eclk", 200 * MHZ); decon_clk_set_parent(dev, "um_decon1_vclk", "disp_pll"); decon_clk_set_rate(dev, "d_decon1_vclk", 134 * MHZ); decon_clk_set_parent(dev, "m_decon1_vclk", "d_decon1_vclk"); } else if (decon->out_type == DECON_OUT_DSI) { /* NOTE: It depends on LCD resolution. default is FHD */ /* ECLK(100Mhz) : using BUS0 PLL */ decon_clk_set_parent(dev, "mout_sclk_decon_ext_eclk", "mout_bus0_pll_top0"); decon_clk_set_rate(dev, "dout_sclk_decon_ext_eclk", 100 * MHZ); decon_clk_set_parent(dev, "m_decon1_eclk", "um_decon1_eclk"); decon_clk_set_rate(dev, "d_decon1_eclk", 100 * MHZ); /* VCLK(142Mhz) : using DISP PLL */ /* decon-int already configures DISP PLL as 142Mhz */ decon_clk_set_parent(dev, "um_decon1_vclk", "disp_pll"); if (decon->lcd_info->mic_enabled) decon_clk_set_rate(dev, "d_decon1_vclk", 72 * MHZ); else decon_clk_set_rate(dev, "d_decon1_vclk", 143 * MHZ); decon_clk_set_parent(dev, "m_decon1_vclk", "d_decon1_vclk"); } else { decon_err("%s: failed to find output device of decon%d\n", __func__, decon->id); } }
/* timeout : usec */ int decon_reg_wait_linecnt_is_zero_timeout(unsigned long timeout) { unsigned long delay_time = 10; unsigned long cnt = timeout / delay_time; u32 linecnt, vstatus; do { linecnt = decon_reg_get_linecnt(); if (!linecnt) { vstatus = decon_reg_get_vstatus(); if ((vstatus != VIDCON1_VSTATUS_BACKPORCH) && (vstatus != VIDCON1_VSTATUS_ACTIVE)) break; } cnt--; udelay(delay_time); } while (cnt); if (!cnt) { decon_err("wait timeout linecount is zero(%u)\n", linecnt); return -EBUSY; } return 0; }
int decon_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct decon_win *win = info->par; struct decon_device *decon = win->decon; int x, y; unsigned long long hz; var->xres_virtual = max(var->xres_virtual, var->xres); var->yres_virtual = max(var->yres_virtual, var->yres); if (!decon_validate_x_alignment(decon, 0, var->xres, var->bits_per_pixel)) return -EINVAL; /* always ensure these are zero, for drop through cases below */ var->transp.offset = 0; var->transp.length = 0; switch (var->bits_per_pixel) { case 1: case 2: case 4: case 8: var->red.offset = 4; var->green.offset = 2; var->blue.offset = 0; var->red.length = 5; var->green.length = 3; var->blue.length = 2; var->transp.offset = 7; var->transp.length = 1; break; case 19: /* 666 with one bit alpha/transparency */ var->transp.offset = 18; var->transp.length = 1; case 18: var->bits_per_pixel = 32; /* 666 format */ var->red.offset = 12; var->green.offset = 6; var->blue.offset = 0; var->red.length = 6; var->green.length = 6; var->blue.length = 6; break; case 16: /* 16 bpp, 565 format */ var->red.offset = 11; var->green.offset = 5; var->blue.offset = 0; var->red.length = 5; var->green.length = 6; var->blue.length = 5; break; case 32: case 28: case 25: var->transp.length = var->bits_per_pixel - 24; var->transp.offset = 24; /* drop through */ case 24: /* our 24bpp is unpacked, so 32bpp */ var->bits_per_pixel = 32; var->red.offset = 16; var->red.length = 8; var->green.offset = 8; var->green.length = 8; var->blue.offset = 0; var->blue.length = 8; break; default: decon_err("invalid bpp %d\n", var->bits_per_pixel); return -EINVAL; } if (decon->pdata->psr_mode == DECON_MIPI_COMMAND_MODE) { x = var->xres; y = var->yres; } else { x = var->xres + var->left_margin + var->right_margin + var->hsync_len; y = var->yres + var->upper_margin + var->lower_margin + var->vsync_len; } hz = 1000000000000ULL; /* 1e12 picoseconds per second */ hz += (x * y) / 2; do_div(hz, x * y); /* divide by x * y with rounding */ hz += var->pixclock / 2; do_div(hz, var->pixclock); /* divide by pixclock with rounding */ win->fps = hz; decon_dbg("xres:%d, yres:%d, v_xres:%d, v_yres:%d, bpp:%d, %lldhz\n", var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel, hz); return 0; }