static void tegra_dc_dsi_suspend(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi; int err; dsi = tegra_dc_get_outdata(dc); tegra_dc_io_start(dc); mutex_lock(&dsi->lock); if (dsi->ulpm) tegra_dsi_exit_ulpm(dsi); err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_suspend_cmd, dsi->info.n_suspend_cmd); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: error while sending dsi suspend cmd\n"); return; } clk_disable(dsi->dsi_clk); mutex_unlock(&dsi->lock); tegra_dc_io_end(dc); }
static bool _tegra_dc_enable(struct tegra_dc *dc) { if (dc->mode.pclk == 0) return false; tegra_dc_io_start(dc); if (dc->out && dc->out->enable) dc->out->enable(); tegra_dc_setup_clk(dc, dc->clk); clk_enable(dc->clk); clk_enable(dc->emc_clk); tegra_periph_reset_deassert(dc->clk); msleep(10); enable_irq(dc->irq); tegra_dc_init(dc); if (dc->out_ops && dc->out_ops->enable) dc->out_ops->enable(dc); /* force a full blending update */ dc->blend.z[0] = -1; tegra_dc_ext_enable(dc->ext); return true; }
static bool _tegra_dc_enable(struct tegra_dc *dc) { if (dc->mode.pclk == 0) { switch (dc->out->type) { case TEGRA_DC_OUT_HDMI: /* DC enable called but no videomode is loaded. Check if HDMI is connected, then set fallback mdoe */ if (tegra_dc_hpd(dc)) { if (_tegra_dc_set_default_videomode(dc)) return false; } else return false; break; /* Do nothing for other outputs for now */ case TEGRA_DC_OUT_RGB: case TEGRA_DC_OUT_DSI: default: return false; } } if (!dc->out) return false; tegra_dc_io_start(dc); return _tegra_dc_controller_enable(dc); }
static void tegra_dc_blend_parallel(struct tegra_dc *dc, struct tegra_dc_blend *blend) { int win_num = dc->gen1_blend_num; unsigned long mask = BIT(win_num) - 1; tegra_dc_io_start(dc); while (mask) { int idx = get_topmost_window(blend->z, &mask, win_num); tegra_dc_writel(dc, WINDOW_A_SELECT << idx, DC_CMD_DISPLAY_WINDOW_HEADER); tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), DC_WIN_BLEND_NOKEY); tegra_dc_writel(dc, BLEND(NOKEY, FIX, 0xff, 0xff), DC_WIN_BLEND_1WIN); tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 0, win_num), DC_WIN_BLEND_2WIN_X); tegra_dc_writel(dc, blend_2win(idx, mask, blend->flags, 1, win_num), DC_WIN_BLEND_2WIN_Y); tegra_dc_writel(dc, blend_3win(idx, mask, blend->flags, win_num), DC_WIN_BLEND_3WIN_XY); } tegra_dc_io_end(dc); }
static void tegra_dc_rgb_disable(struct tegra_dc *dc) { tegra_dc_io_start(dc); tegra_dc_writel(dc, 0x00000000, DC_CMD_DISPLAY_POWER_CONTROL); tegra_dc_write_table(dc, tegra_dc_rgb_disable_pintable); tegra_dc_io_end(dc); }
static void tegra_dc_dsi_enable(struct tegra_dc *dc) { struct tegra_dc_dsi_data *dsi = tegra_dc_get_outdata(dc); int err; tegra_dc_io_start(dc); mutex_lock(&dsi->lock); if (dsi->ulpm) { tegra_dsi_exit_ulpm(dsi); if (dsi->info.panel_reset) { err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, dsi->info.n_init_cmd); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: error while sending dsi init cmd\n"); return; } } } else { err = tegra_dsi_init_hw(dc, dsi); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: not able to init dsi hardware\n"); return; } err = tegra_dsi_set_to_lp_mode(dc, dsi); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: not able to set to lp mode\n"); return; } err = tegra_dsi_send_panel_cmd(dc, dsi, dsi->info.dsi_init_cmd, dsi->info.n_init_cmd); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: error while sending dsi init cmd\n"); return; } err = tegra_dsi_set_to_hs_mode(dc, dsi); if (err < 0) { dev_err(&dc->ndev->dev, "dsi: not able to set to hs mode\n"); return; } } if (dsi->status.driven == DSI_DRIVEN_MODE_DC) { tegra_dsi_start_dc_stream(dc, dsi); } mutex_unlock(&dsi->lock); tegra_dc_io_end(dc); }
static void tegra_dc_blend_sequential(struct tegra_dc *dc, struct tegra_dc_blend *blend) { int i; tegra_dc_io_start(dc); for (i = 0; i < DC_N_WINDOWS; i++) { if (!tegra_dc_feature_is_gen2_blender(dc, i)) continue; tegra_dc_writel(dc, WINDOW_A_SELECT << i, DC_CMD_DISPLAY_WINDOW_HEADER); if (blend->flags[i] & TEGRA_WIN_FLAG_BLEND_COVERAGE) { tegra_dc_writel(dc, WIN_K1(0xff) | WIN_K2(0xff) | WIN_BLEND_ENABLE, DC_WINBUF_BLEND_LAYER_CONTROL); tegra_dc_writel(dc, WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_SRC | WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC | WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 | WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO, DC_WINBUF_BLEND_MATCH_SELECT); tegra_dc_writel(dc, WIN_ALPHA_1BIT_WEIGHT0(0) | WIN_ALPHA_1BIT_WEIGHT1(0xff), DC_WINBUF_BLEND_ALPHA_1BIT); } else if (blend->flags[i] & TEGRA_WIN_FLAG_BLEND_PREMULT) { tegra_dc_writel(dc, WIN_K1(0xff) | WIN_K2(0xff) | WIN_BLEND_ENABLE, DC_WINBUF_BLEND_LAYER_CONTROL); tegra_dc_writel(dc, WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 | WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1 | WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 | WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO, DC_WINBUF_BLEND_MATCH_SELECT); tegra_dc_writel(dc, WIN_ALPHA_1BIT_WEIGHT0(0) | WIN_ALPHA_1BIT_WEIGHT1(0xff), DC_WINBUF_BLEND_ALPHA_1BIT); } else { tegra_dc_writel(dc, WIN_BLEND_BYPASS, DC_WINBUF_BLEND_LAYER_CONTROL); } } tegra_dc_io_end(dc); }
int tegra_dc_ext_set_cursor(struct tegra_dc_ext_user *user, struct tegra_dc_ext_cursor *args) { struct tegra_dc_ext *ext = user->ext; struct tegra_dc *dc = ext->dc; u32 val; bool enable; int ret; mutex_lock(&ext->cursor.lock); if (ext->cursor.user != user) { ret = -EACCES; goto unlock; } if (!ext->enabled) { ret = -ENXIO; goto unlock; } enable = !!(args->flags & TEGRA_DC_EXT_CURSOR_FLAGS_VISIBLE); mutex_lock(&dc->lock); tegra_dc_io_start(dc); tegra_dc_hold_dc_out(dc); val = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS); if (!!(val & CURSOR_ENABLE) != enable) { val &= ~CURSOR_ENABLE; if (enable) val |= CURSOR_ENABLE; tegra_dc_writel(dc, val, DC_DISP_DISP_WIN_OPTIONS); } tegra_dc_writel(dc, CURSOR_POSITION(args->x, args->y), DC_DISP_CURSOR_POSITION); tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); /* TODO: need to sync here? hopefully can avoid this, but need to * figure out interaction w/ rest of GENERAL_ACT_REQ */ tegra_dc_release_dc_out(dc); tegra_dc_io_end(dc); mutex_unlock(&dc->lock); mutex_unlock(&ext->cursor.lock); return 0; unlock: mutex_unlock(&ext->cursor.lock); return ret; }
void tegra_dc_enable_crc(struct tegra_dc *dc) { u32 val; tegra_dc_io_start(dc); val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA | CRC_ENABLE_ENABLE; tegra_dc_writel(dc, val, DC_COM_CRC_CONTROL); tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); }
static bool nvsd_phase_in_adjustments(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) { u8 step, cur_sd_brightness; u16 target_k, cur_k; u32 man_k, val; cur_sd_brightness = atomic_read(_sd_brightness); target_k = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); target_k = SD_HW_K_R(target_k); cur_k = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES); cur_k = SD_HW_K_R(cur_k); /* read brightness value */ val = tegra_dc_readl(dc, DC_DISP_SD_BL_CONTROL); val = SD_BLC_BRIGHTNESS(val); step = settings->phase_adj_step; if (cur_sd_brightness != val || target_k != cur_k) { if (!step) step = ADJ_PHASE_STEP; /* Phase in Backlight and Pixel K every ADJ_PHASE_STEP frames*/ if ((step-- & ADJ_PHASE_STEP) == ADJ_PHASE_STEP) { if (val != cur_sd_brightness) { val > cur_sd_brightness ? (cur_sd_brightness++) : (cur_sd_brightness--); } if (target_k != cur_k) { if (target_k > cur_k) cur_k += K_STEP; else cur_k -= K_STEP; } /* Set manual k value */ man_k = SD_MAN_K_R(cur_k) | SD_MAN_K_G(cur_k) | SD_MAN_K_B(cur_k); tegra_dc_io_start(dc); tegra_dc_writel(dc, man_k, DC_DISP_SD_MAN_K_VALUES); tegra_dc_io_end(dc); /* Set manual brightness value */ atomic_set(_sd_brightness, cur_sd_brightness); } settings->phase_adj_step = step; return true; } else return false; }
int tegra_dc_config_frame_end_intr(struct tegra_dc *dc, bool enable) { tegra_dc_io_start(dc); tegra_dc_writel(dc, FRAME_END_INT, DC_CMD_INT_STATUS); if (enable) { atomic_inc(&frame_end_ref); tegra_dc_unmask_interrupt(dc, FRAME_END_INT); } else if (!atomic_dec_return(&frame_end_ref)) tegra_dc_mask_interrupt(dc, FRAME_END_INT); tegra_dc_io_end(dc); return 0; }
static irqreturn_t tegra_dc_irq(int irq, void *ptr) { #ifndef CONFIG_TEGRA_FPGA_PLATFORM struct tegra_dc *dc = ptr; unsigned long status; unsigned long underflow_mask; u32 val; if (!nvhost_module_powered_ext(nvhost_get_parent(dc->ndev))) { WARN(1, "IRQ when DC not powered!\n"); tegra_dc_io_start(dc); status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); tegra_dc_writel(dc, status, DC_CMD_INT_STATUS); tegra_dc_io_end(dc); return IRQ_HANDLED; } /* clear all status flags except underflow, save those for the worker */ status = tegra_dc_readl(dc, DC_CMD_INT_STATUS); tegra_dc_writel(dc, status & ~ALL_UF_INT, DC_CMD_INT_STATUS); val = tegra_dc_readl(dc, DC_CMD_INT_MASK); tegra_dc_writel(dc, val & ~ALL_UF_INT, DC_CMD_INT_MASK); /* * Overlays can get thier internal state corrupted during and underflow * condition. The only way to fix this state is to reset the DC. * if we get 4 consecutive frames with underflows, assume we're * hosed and reset. */ underflow_mask = status & ALL_UF_INT; /* Check underflow */ if (underflow_mask) { dc->underflow_mask |= underflow_mask; schedule_delayed_work(&dc->underflow_work, msecs_to_jiffies(1)); } if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) tegra_dc_one_shot_irq(dc, status); else tegra_dc_continuous_irq(dc, status); return IRQ_HANDLED; #else /* CONFIG_TEGRA_FPGA_PLATFORM */ return IRQ_NONE; #endif /* !CONFIG_TEGRA_FPGA_PLATFORM */ }
static bool _tegra_dc_enable(struct tegra_dc *dc) { if (dc->mode.pclk == 0) return false; if (!dc->out) return false; tegra_dc_io_start(dc); if (!_tegra_dc_controller_enable(dc)) { tegra_dc_io_end(dc); return false; } return true; }
void tegra_dc_enable_crc(struct tegra_dc *dc) { u32 val; mutex_lock(&dc->lock); tegra_dc_hold_dc_out(dc); tegra_dc_io_start(dc); val = CRC_ALWAYS_ENABLE | CRC_INPUT_DATA_ACTIVE_DATA | CRC_ENABLE_ENABLE; tegra_dc_writel(dc, val, DC_COM_CRC_CONTROL); tegra_dc_writel(dc, GENERAL_UPDATE, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); tegra_dc_release_dc_out(dc); mutex_unlock(&dc->lock); }
int tegra_dc_ext_cursor_clip(struct tegra_dc_ext_user *user, int *args) { struct tegra_dc_ext *ext = user->ext; struct tegra_dc *dc = ext->dc; int ret; unsigned long reg_val; mutex_lock(&ext->cursor.lock); if (ext->cursor.user != user) { ret = -EACCES; goto unlock; } if (!ext->enabled) { ret = -ENXIO; goto unlock; } mutex_lock(&dc->lock); tegra_dc_io_start(dc); tegra_dc_hold_dc_out(dc); reg_val = tegra_dc_readl(dc, DC_DISP_CURSOR_START_ADDR); reg_val &= ~CURSOR_CLIP_SHIFT_BITS(3); /* Clear out the old value */ tegra_dc_writel(dc, reg_val | CURSOR_CLIP_SHIFT_BITS(*args), DC_DISP_CURSOR_START_ADDR); tegra_dc_release_dc_out(dc); tegra_dc_io_end(dc); mutex_unlock(&dc->lock); mutex_unlock(&ext->cursor.lock); return 0; unlock: mutex_unlock(&ext->cursor.lock); return ret; }
static ssize_t nvsd_registers_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct device *dev = container_of((kobj->parent), struct device, kobj); struct nvhost_device *ndev = to_nvhost_device(dev); struct tegra_dc *dc = nvhost_get_drvdata(ndev); ssize_t res = 0; clk_enable(dc->clk); tegra_dc_io_start(dc); mutex_lock(&dc->lock); if (!dc->enabled) { mutex_unlock(&dc->lock); return -ENODEV; } mutex_unlock(&dc->lock); NVSD_PRINT_REG(DC_DISP_SD_CONTROL); NVSD_PRINT_REG(DC_DISP_SD_CSC_COEFF); NVSD_PRINT_REG_ARRAY(DC_DISP_SD_LUT); NVSD_PRINT_REG(DC_DISP_SD_FLICKER_CONTROL); NVSD_PRINT_REG(DC_DISP_SD_PIXEL_COUNT); NVSD_PRINT_REG_ARRAY(DC_DISP_SD_HISTOGRAM); NVSD_PRINT_REG(DC_DISP_SD_BL_PARAMETERS); NVSD_PRINT_REG_ARRAY(DC_DISP_SD_BL_TF); NVSD_PRINT_REG(DC_DISP_SD_BL_CONTROL); NVSD_PRINT_REG(DC_DISP_SD_HW_K_VALUES); NVSD_PRINT_REG(DC_DISP_SD_MAN_K_VALUES); #ifdef CONFIG_TEGRA_SD_GEN2 NVSD_PRINT_REG(DC_DISP_SD_K_LIMIT); NVSD_PRINT_REG(DC_DISP_SD_WINDOW_POSITION); NVSD_PRINT_REG(DC_DISP_SD_WINDOW_SIZE); NVSD_PRINT_REG(DC_DISP_SD_SOFT_CLIPPING); NVSD_PRINT_REG(DC_DISP_SD_SMOOTH_K); #endif tegra_dc_io_end(dc); clk_disable(dc->clk); return res; }
int tegra_dc_update_csc(struct tegra_dc *dc, int win_idx) { mutex_lock(&dc->lock); if (!dc->enabled) { mutex_unlock(&dc->lock); return -EFAULT; } tegra_dc_io_start(dc); tegra_dc_hold_dc_out(dc); tegra_dc_writel(dc, WINDOW_A_SELECT << win_idx, DC_CMD_DISPLAY_WINDOW_HEADER); tegra_dc_set_csc(dc, &dc->windows[win_idx].csc); tegra_dc_release_dc_out(dc); tegra_dc_io_end(dc); mutex_unlock(&dc->lock); return 0; }
static int dbg_dsi_show(struct seq_file *s, void *unused) { struct tegra_dc_dsi_data *dsi = s->private; unsigned long i = 0, j = 0; u32 col = 0; u32 base[MAX_DSI_INSTANCE] = {TEGRA_DSI_BASE, TEGRA_DSIB_BASE}; if (!dsi->enabled) { seq_puts(s, "DSI controller suspended\n"); return 0; } tegra_dc_io_start(dsi->dc); tegra_dsi_clk_enable(dsi); /* mem dd dump */ for (i = 0; i < dsi->max_instances; i++) { for (col = 0, j = 0; j < 0x64; j++) { if (col == 0) seq_printf(s, "%08lX:", base[i] + 4*j); seq_printf(s, "%c%08lX", col == 2 ? '-' : ' ', tegra_dsi_controller_readl(dsi, j, i)); if (col == 3) { seq_puts(s, "\n"); col = 0; } else col++; } seq_puts(s, "\n"); } #define DUMP_REG(a) seq_printf(s, "%-45s | %#05x | %#010lx |\n", \ #a, a, tegra_dsi_readl(dsi, a)); DUMP_REG(DSI_INCR_SYNCPT_CNTRL); DUMP_REG(DSI_INCR_SYNCPT_ERROR); DUMP_REG(DSI_CTXSW); DUMP_REG(DSI_POWER_CONTROL); DUMP_REG(DSI_INT_ENABLE); DUMP_REG(DSI_HOST_DSI_CONTROL); DUMP_REG(DSI_CONTROL); DUMP_REG(DSI_SOL_DELAY); DUMP_REG(DSI_MAX_THRESHOLD); DUMP_REG(DSI_TRIGGER); DUMP_REG(DSI_TX_CRC); DUMP_REG(DSI_STATUS); DUMP_REG(DSI_INIT_SEQ_CONTROL); DUMP_REG(DSI_INIT_SEQ_DATA_0); DUMP_REG(DSI_INIT_SEQ_DATA_1); DUMP_REG(DSI_INIT_SEQ_DATA_2); DUMP_REG(DSI_INIT_SEQ_DATA_3); DUMP_REG(DSI_INIT_SEQ_DATA_4); DUMP_REG(DSI_INIT_SEQ_DATA_5); DUMP_REG(DSI_INIT_SEQ_DATA_6); DUMP_REG(DSI_INIT_SEQ_DATA_7); DUMP_REG(DSI_PKT_SEQ_0_LO); DUMP_REG(DSI_PKT_SEQ_0_HI); DUMP_REG(DSI_PKT_SEQ_1_LO); DUMP_REG(DSI_PKT_SEQ_1_HI); DUMP_REG(DSI_PKT_SEQ_2_LO); DUMP_REG(DSI_PKT_SEQ_2_HI); DUMP_REG(DSI_PKT_SEQ_3_LO); DUMP_REG(DSI_PKT_SEQ_3_HI); DUMP_REG(DSI_PKT_SEQ_4_LO); DUMP_REG(DSI_PKT_SEQ_4_HI); DUMP_REG(DSI_PKT_SEQ_5_LO); DUMP_REG(DSI_PKT_SEQ_5_HI); DUMP_REG(DSI_DCS_CMDS); DUMP_REG(DSI_PKT_LEN_0_1); DUMP_REG(DSI_PKT_LEN_2_3); DUMP_REG(DSI_PKT_LEN_4_5); DUMP_REG(DSI_PKT_LEN_6_7); DUMP_REG(DSI_PHY_TIMING_0); DUMP_REG(DSI_PHY_TIMING_1); DUMP_REG(DSI_PHY_TIMING_2); DUMP_REG(DSI_BTA_TIMING); DUMP_REG(DSI_TIMEOUT_0); DUMP_REG(DSI_TIMEOUT_1); DUMP_REG(DSI_TO_TALLY); DUMP_REG(DSI_PAD_CONTROL); DUMP_REG(DSI_PAD_CONTROL_CD); DUMP_REG(DSI_PAD_CD_STATUS); DUMP_REG(DSI_VID_MODE_CONTROL); DUMP_REG(DSI_PAD_CONTROL_0_VS1); DUMP_REG(DSI_PAD_CONTROL_CD_VS1); DUMP_REG(DSI_PAD_CD_STATUS_VS1); DUMP_REG(DSI_PAD_CONTROL_1_VS1); DUMP_REG(DSI_PAD_CONTROL_2_VS1); DUMP_REG(DSI_PAD_CONTROL_3_VS1); DUMP_REG(DSI_PAD_CONTROL_4_VS1); DUMP_REG(DSI_GANGED_MODE_CONTROL); DUMP_REG(DSI_GANGED_MODE_START); DUMP_REG(DSI_GANGED_MODE_SIZE); #undef DUMP_REG tegra_dsi_clk_disable(dsi); tegra_dc_io_end(dsi->dc); return 0; }
int tegra_dc_ext_set_cursor_image(struct tegra_dc_ext_user *user, struct tegra_dc_ext_cursor_image *args) { struct tegra_dc_ext *ext = user->ext; struct tegra_dc *dc = ext->dc; struct nvmap_handle_ref *handle, *old_handle; dma_addr_t phys_addr; u32 size; int ret; if (!user->nvmap) return -EFAULT; size = TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE(args->flags); #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 && size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64) return -EINVAL; #else if (size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_32x32 && size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_64x64 && size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_128x128 && size != TEGRA_DC_EXT_CURSOR_IMAGE_FLAGS_SIZE_256x256) return -EINVAL; #endif #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) if (args->flags && TEGRA_DC_EXT_CURSOR_FLAGS_RGBA_NORMAL) return -EINVAL; #endif mutex_lock(&ext->cursor.lock); if (ext->cursor.user != user) { ret = -EACCES; goto unlock; } if (!ext->enabled) { ret = -ENXIO; goto unlock; } old_handle = ext->cursor.cur_handle; ret = tegra_dc_ext_pin_window(user, args->buff_id, &handle, &phys_addr); if (ret) goto unlock; ext->cursor.cur_handle = handle; mutex_lock(&dc->lock); tegra_dc_io_start(dc); tegra_dc_hold_dc_out(dc); set_cursor_image_hw(dc, args, phys_addr); tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); tegra_dc_release_dc_out(dc); tegra_dc_io_end(dc); /* XXX sync here? */ mutex_unlock(&dc->lock); mutex_unlock(&ext->cursor.lock); if (old_handle) { nvmap_unpin(ext->nvmap, old_handle); nvmap_free(ext->nvmap, old_handle); } return 0; unlock: mutex_unlock(&ext->cursor.lock); return ret; }
static void handle_check_edid_l(struct tegra_dc_hdmi_data *hdmi) { struct fb_monspecs specs; #ifdef CONFIG_SWITCH int state; #endif memset(&specs, 0, sizeof(specs)); #ifdef CONFIG_FRAMEBUFFER_CONSOLE /* Set default videomode on dc before enabling it*/ tegra_dc_set_default_videomode(hdmi->dc); #endif if (!tegra_dc_hpd(work_state.hdmi->dc)) { /* hpd dropped - stop EDID read */ pr_info("hpd == 0, aborting EDID read\n"); goto end_disabled; } if (tegra_edid_get_monspecs(hdmi->edid, &specs)) { /* Failed to read EDID. If we still have retry attempts left, * schedule another attempt. Otherwise give up and just go to * the disabled state. */ work_state.edid_reads++; if (work_state.edid_reads >= MAX_EDID_READ_ATTEMPTS) { pr_info("Failed to read EDID after %d times. Giving up.\n", work_state.edid_reads); goto end_disabled; } else { hdmi_state_machine_set_state_l(HDMI_STATE_CHECK_EDID, CHECK_EDID_DELAY_MS); } return; } if (tegra_edid_get_eld(hdmi->edid, &hdmi->eld) < 0) { pr_err("error populating eld\n"); goto end_disabled; } hdmi->eld_retrieved = true; pr_info("panel size %d by %d\n", specs.max_x, specs.max_y); /* monitors like to lie about these but they are still useful for * detecting aspect ratios */ hdmi->dc->out->h_size = specs.max_x * 1000; hdmi->dc->out->v_size = specs.max_y * 1000; hdmi->dvi = !(specs.misc & FB_MISC_HDMI); #ifdef CONFIG_ADF_TEGRA tegra_adf_process_hotplug_connected(hdmi->dc->adf, &specs); #endif #ifdef CONFIG_TEGRA_DC_EXTENSIONS tegra_fb_update_monspecs(hdmi->dc->fb, &specs, tegra_dc_hdmi_mode_filter); #endif #ifdef CONFIG_SWITCH state = tegra_edid_audio_supported(hdmi->edid) ? 1 : 0; switch_set_state(&hdmi->audio_switch, state); pr_info("%s: audio_switch %d\n", __func__, state); switch_set_state(&hdmi->hpd_switch, 1); pr_info("Display connected, hpd_switch 1\n"); #endif hdmi->dc->connected = true; #ifdef CONFIG_TEGRA_DC_EXTENSIONS tegra_dc_ext_process_hotplug(hdmi->dc->ndev->id); #endif if (unlikely(tegra_is_clk_enabled(hdmi->clk))) { /* the only time this should happen is on boot, where the * sequence is that hdmi is enabled before EDID is read. * hdmi_enable() doesn't have EDID information yet so can't * setup audio and infoframes, so we have to do so here. */ pr_info("%s: setting audio and infoframes\n", __func__); tegra_dc_io_start(hdmi->dc); tegra_dc_hdmi_setup_audio_and_infoframes(hdmi->dc); tegra_dc_io_end(hdmi->dc); } hdmi_state_machine_set_state_l(HDMI_STATE_DONE_ENABLED, -1); return; end_disabled: hdmi->eld_retrieved = false; hdmi_disable_l(hdmi); hdmi_state_machine_set_state_l(HDMI_STATE_DONE_DISABLED, -1); }
static void _dump_regs(struct tegra_dc *dc, void *data, void (* print)(void *data, const char *str)) { int i; char buff[256]; tegra_dc_io_start(dc); clk_enable(dc->clk); DUMP_REG(DC_CMD_DISPLAY_COMMAND_OPTION0); DUMP_REG(DC_CMD_DISPLAY_COMMAND); DUMP_REG(DC_CMD_SIGNAL_RAISE); DUMP_REG(DC_CMD_INT_STATUS); DUMP_REG(DC_CMD_INT_MASK); DUMP_REG(DC_CMD_INT_ENABLE); DUMP_REG(DC_CMD_INT_TYPE); DUMP_REG(DC_CMD_INT_POLARITY); DUMP_REG(DC_CMD_SIGNAL_RAISE1); DUMP_REG(DC_CMD_SIGNAL_RAISE2); DUMP_REG(DC_CMD_SIGNAL_RAISE3); DUMP_REG(DC_CMD_STATE_ACCESS); DUMP_REG(DC_CMD_STATE_CONTROL); DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); DUMP_REG(DC_CMD_REG_ACT_CONTROL); DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS0); DUMP_REG(DC_DISP_DISP_SIGNAL_OPTIONS1); DUMP_REG(DC_DISP_DISP_WIN_OPTIONS); DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY); DUMP_REG(DC_DISP_MEM_HIGH_PRIORITY_TIMER); DUMP_REG(DC_DISP_DISP_TIMING_OPTIONS); DUMP_REG(DC_DISP_REF_TO_SYNC); DUMP_REG(DC_DISP_SYNC_WIDTH); DUMP_REG(DC_DISP_BACK_PORCH); DUMP_REG(DC_DISP_DISP_ACTIVE); DUMP_REG(DC_DISP_FRONT_PORCH); DUMP_REG(DC_DISP_H_PULSE0_CONTROL); DUMP_REG(DC_DISP_H_PULSE0_POSITION_A); DUMP_REG(DC_DISP_H_PULSE0_POSITION_B); DUMP_REG(DC_DISP_H_PULSE0_POSITION_C); DUMP_REG(DC_DISP_H_PULSE0_POSITION_D); DUMP_REG(DC_DISP_H_PULSE1_CONTROL); DUMP_REG(DC_DISP_H_PULSE1_POSITION_A); DUMP_REG(DC_DISP_H_PULSE1_POSITION_B); DUMP_REG(DC_DISP_H_PULSE1_POSITION_C); DUMP_REG(DC_DISP_H_PULSE1_POSITION_D); DUMP_REG(DC_DISP_H_PULSE2_CONTROL); DUMP_REG(DC_DISP_H_PULSE2_POSITION_A); DUMP_REG(DC_DISP_H_PULSE2_POSITION_B); DUMP_REG(DC_DISP_H_PULSE2_POSITION_C); DUMP_REG(DC_DISP_H_PULSE2_POSITION_D); DUMP_REG(DC_DISP_V_PULSE0_CONTROL); DUMP_REG(DC_DISP_V_PULSE0_POSITION_A); DUMP_REG(DC_DISP_V_PULSE0_POSITION_B); DUMP_REG(DC_DISP_V_PULSE0_POSITION_C); DUMP_REG(DC_DISP_V_PULSE1_CONTROL); DUMP_REG(DC_DISP_V_PULSE1_POSITION_A); DUMP_REG(DC_DISP_V_PULSE1_POSITION_B); DUMP_REG(DC_DISP_V_PULSE1_POSITION_C); DUMP_REG(DC_DISP_V_PULSE2_CONTROL); DUMP_REG(DC_DISP_V_PULSE2_POSITION_A); DUMP_REG(DC_DISP_V_PULSE3_CONTROL); DUMP_REG(DC_DISP_V_PULSE3_POSITION_A); DUMP_REG(DC_DISP_M0_CONTROL); DUMP_REG(DC_DISP_M1_CONTROL); DUMP_REG(DC_DISP_DI_CONTROL); DUMP_REG(DC_DISP_PP_CONTROL); DUMP_REG(DC_DISP_PP_SELECT_A); DUMP_REG(DC_DISP_PP_SELECT_B); DUMP_REG(DC_DISP_PP_SELECT_C); DUMP_REG(DC_DISP_PP_SELECT_D); DUMP_REG(DC_DISP_DISP_CLOCK_CONTROL); DUMP_REG(DC_DISP_DISP_INTERFACE_CONTROL); DUMP_REG(DC_DISP_DISP_COLOR_CONTROL); DUMP_REG(DC_DISP_SHIFT_CLOCK_OPTIONS); DUMP_REG(DC_DISP_DATA_ENABLE_OPTIONS); DUMP_REG(DC_DISP_SERIAL_INTERFACE_OPTIONS); DUMP_REG(DC_DISP_LCD_SPI_OPTIONS); DUMP_REG(DC_DISP_BORDER_COLOR); DUMP_REG(DC_DISP_COLOR_KEY0_LOWER); DUMP_REG(DC_DISP_COLOR_KEY0_UPPER); DUMP_REG(DC_DISP_COLOR_KEY1_LOWER); DUMP_REG(DC_DISP_COLOR_KEY1_UPPER); DUMP_REG(DC_DISP_CURSOR_FOREGROUND); DUMP_REG(DC_DISP_CURSOR_BACKGROUND); DUMP_REG(DC_DISP_CURSOR_START_ADDR); DUMP_REG(DC_DISP_CURSOR_START_ADDR_NS); DUMP_REG(DC_DISP_CURSOR_POSITION); DUMP_REG(DC_DISP_CURSOR_POSITION_NS); DUMP_REG(DC_DISP_INIT_SEQ_CONTROL); DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_A); DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_B); DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_C); DUMP_REG(DC_DISP_SPI_INIT_SEQ_DATA_D); DUMP_REG(DC_DISP_DC_MCCIF_FIFOCTRL); DUMP_REG(DC_DISP_MCCIF_DISPLAY0A_HYST); DUMP_REG(DC_DISP_MCCIF_DISPLAY0B_HYST); DUMP_REG(DC_DISP_MCCIF_DISPLAY0C_HYST); DUMP_REG(DC_DISP_MCCIF_DISPLAY1B_HYST); DUMP_REG(DC_DISP_DAC_CRT_CTRL); DUMP_REG(DC_DISP_DISP_MISC_CONTROL); for (i = 0; i < 3; i++) { print(data, "\n"); snprintf(buff, sizeof(buff), "WINDOW %c:\n", 'A' + i); print(data, buff); tegra_dc_writel(dc, WINDOW_A_SELECT << i, DC_CMD_DISPLAY_WINDOW_HEADER); DUMP_REG(DC_CMD_DISPLAY_WINDOW_HEADER); DUMP_REG(DC_WIN_WIN_OPTIONS); DUMP_REG(DC_WIN_BYTE_SWAP); DUMP_REG(DC_WIN_BUFFER_CONTROL); DUMP_REG(DC_WIN_COLOR_DEPTH); DUMP_REG(DC_WIN_POSITION); DUMP_REG(DC_WIN_SIZE); DUMP_REG(DC_WIN_PRESCALED_SIZE); DUMP_REG(DC_WIN_H_INITIAL_DDA); DUMP_REG(DC_WIN_V_INITIAL_DDA); DUMP_REG(DC_WIN_DDA_INCREMENT); DUMP_REG(DC_WIN_LINE_STRIDE); DUMP_REG(DC_WIN_BUF_STRIDE); DUMP_REG(DC_WIN_UV_BUF_STRIDE); DUMP_REG(DC_WIN_BLEND_NOKEY); DUMP_REG(DC_WIN_BLEND_1WIN); DUMP_REG(DC_WIN_BLEND_2WIN_X); DUMP_REG(DC_WIN_BLEND_2WIN_Y); DUMP_REG(DC_WIN_BLEND_3WIN_XY); DUMP_REG(DC_WINBUF_START_ADDR); DUMP_REG(DC_WINBUF_START_ADDR_U); DUMP_REG(DC_WINBUF_START_ADDR_V); DUMP_REG(DC_WINBUF_ADDR_H_OFFSET); DUMP_REG(DC_WINBUF_ADDR_V_OFFSET); DUMP_REG(DC_WINBUF_UFLOW_STATUS); DUMP_REG(DC_WIN_CSC_YOF); DUMP_REG(DC_WIN_CSC_KYRGB); DUMP_REG(DC_WIN_CSC_KUR); DUMP_REG(DC_WIN_CSC_KVR); DUMP_REG(DC_WIN_CSC_KUG); DUMP_REG(DC_WIN_CSC_KVG); DUMP_REG(DC_WIN_CSC_KUB); DUMP_REG(DC_WIN_CSC_KVB); } clk_disable(dc->clk); tegra_dc_io_end(dc); }
/* Does not support updating windows on multiple dcs in one call. * Requires a matching sync_windows to avoid leaking ref-count on clocks. */ int tegra_dc_update_windows(struct tegra_dc_win *windows[], int n) { struct tegra_dc *dc; unsigned long update_mask = GENERAL_ACT_REQ; unsigned long win_options; bool update_blend_par = false; bool update_blend_seq = false; int i; dc = windows[0]->dc; trace_update_windows(dc); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) { /* Acquire one_shot_lock to avoid race condition between * cancellation of old delayed work and schedule of new * delayed work. */ mutex_lock(&dc->one_shot_lock); cancel_delayed_work_sync(&dc->one_shot_work); } mutex_lock(&dc->lock); if (!dc->enabled) { mutex_unlock(&dc->lock); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) mutex_unlock(&dc->one_shot_lock); return -EFAULT; } tegra_dc_io_start(dc); tegra_dc_hold_dc_out(dc); if (no_vsync) tegra_dc_writel(dc, WRITE_MUX_ACTIVE | READ_MUX_ACTIVE, DC_CMD_STATE_ACCESS); else tegra_dc_writel(dc, WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY, DC_CMD_STATE_ACCESS); for (i = 0; i < n; i++) { struct tegra_dc_win *win = windows[i]; struct tegra_dc_win *dc_win = tegra_dc_get_window(dc, win->idx); bool scan_column = 0; fixed20_12 h_offset, v_offset; bool invert_h = (win->flags & TEGRA_WIN_FLAG_INVERT_H) != 0; bool invert_v = (win->flags & TEGRA_WIN_FLAG_INVERT_V) != 0; bool yuv = tegra_dc_is_yuv(win->fmt); bool yuvp = tegra_dc_is_yuv_planar(win->fmt); unsigned Bpp = tegra_dc_fmt_bpp(win->fmt) / 8; /* Bytes per pixel of bandwidth, used for dda_inc calculation */ unsigned Bpp_bw = Bpp * (yuvp ? 2 : 1); const bool filter_h = win_use_h_filter(dc, win); const bool filter_v = win_use_v_filter(dc, win); #if defined(CONFIG_TEGRA_DC_SCAN_COLUMN) scan_column = (win->flags & TEGRA_WIN_FLAG_SCAN_COLUMN); #endif /* Update blender */ if ((win->z != dc->blend.z[win->idx]) || ((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) != dc->blend.flags[win->idx])) { dc->blend.z[win->idx] = win->z; dc->blend.flags[win->idx] = win->flags & TEGRA_WIN_BLEND_FLAGS_MASK; if (tegra_dc_feature_is_gen2_blender(dc, win->idx)) update_blend_seq = true; else update_blend_par = true; } tegra_dc_writel(dc, WINDOW_A_SELECT << win->idx, DC_CMD_DISPLAY_WINDOW_HEADER); if (!no_vsync) update_mask |= WIN_A_ACT_REQ << win->idx; if (!WIN_IS_ENABLED(win)) { dc_win->dirty = no_vsync ? 0 : 1; tegra_dc_writel(dc, 0, DC_WIN_WIN_OPTIONS); continue; } tegra_dc_writel(dc, win->fmt & 0x1f, DC_WIN_COLOR_DEPTH); tegra_dc_writel(dc, win->fmt >> 6, DC_WIN_BYTE_SWAP); tegra_dc_writel(dc, V_POSITION(win->out_y) | H_POSITION(win->out_x), DC_WIN_POSITION); tegra_dc_writel(dc, V_SIZE(win->out_h) | H_SIZE(win->out_w), DC_WIN_SIZE); /* Check scan_column flag to set window size and scaling. */ win_options = WIN_ENABLE; if (scan_column) { win_options |= WIN_SCAN_COLUMN; win_options |= H_FILTER_ENABLE(filter_v); win_options |= V_FILTER_ENABLE(filter_h); } else { win_options |= H_FILTER_ENABLE(filter_h); win_options |= V_FILTER_ENABLE(filter_v); } /* Update scaling registers if window supports scaling. */ if (likely(tegra_dc_feature_has_scaling(dc, win->idx))) tegra_dc_update_scaling(dc, win, Bpp, Bpp_bw, scan_column); #if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC) tegra_dc_writel(dc, 0, DC_WIN_BUF_STRIDE); tegra_dc_writel(dc, 0, DC_WIN_UV_BUF_STRIDE); #endif tegra_dc_writel(dc, (unsigned long)win->phys_addr, DC_WINBUF_START_ADDR); if (!yuvp) { tegra_dc_writel(dc, win->stride, DC_WIN_LINE_STRIDE); } else { tegra_dc_writel(dc, (unsigned long)win->phys_addr_u, DC_WINBUF_START_ADDR_U); tegra_dc_writel(dc, (unsigned long)win->phys_addr_v, DC_WINBUF_START_ADDR_V); tegra_dc_writel(dc, LINE_STRIDE(win->stride) | UV_LINE_STRIDE(win->stride_uv), DC_WIN_LINE_STRIDE); } if (invert_h) { h_offset.full = win->x.full + win->w.full; h_offset.full = dfixed_floor(h_offset) * Bpp; h_offset.full -= dfixed_const(1); } else { h_offset.full = dfixed_floor(win->x) * Bpp; } v_offset = win->y; if (invert_v) { v_offset.full += win->h.full - dfixed_const(1); } tegra_dc_writel(dc, dfixed_trunc(h_offset), DC_WINBUF_ADDR_H_OFFSET); tegra_dc_writel(dc, dfixed_trunc(v_offset), DC_WINBUF_ADDR_V_OFFSET); if (tegra_dc_feature_has_tiling(dc, win->idx)) { if (WIN_IS_TILED(win)) tegra_dc_writel(dc, DC_WIN_BUFFER_ADDR_MODE_TILE | DC_WIN_BUFFER_ADDR_MODE_TILE_UV, DC_WIN_BUFFER_ADDR_MODE); else tegra_dc_writel(dc, DC_WIN_BUFFER_ADDR_MODE_LINEAR | DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV, DC_WIN_BUFFER_ADDR_MODE); } if (yuv) win_options |= CSC_ENABLE; else if (tegra_dc_fmt_bpp(win->fmt) < 24) win_options |= COLOR_EXPAND; #if defined(CONFIG_ARCH_TEGRA_3x_SOC) || defined(CONFIG_ARCH_TEGRA_11x_SOC) if (win->global_alpha == 255) { tegra_dc_writel(dc, 0, DC_WIN_GLOBAL_ALPHA); } else { tegra_dc_writel(dc, GLOBAL_ALPHA_ENABLE | win->global_alpha, DC_WIN_GLOBAL_ALPHA); win_options |= CP_ENABLE; } #endif if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE) win_options |= CP_ENABLE; win_options |= H_DIRECTION_DECREMENT(invert_h); win_options |= V_DIRECTION_DECREMENT(invert_v); tegra_dc_writel(dc, win_options, DC_WIN_WIN_OPTIONS); dc_win->dirty = no_vsync ? 0 : 1; trace_window_update(dc, win); } if (update_blend_par || update_blend_seq) { if (update_blend_par) tegra_dc_blend_parallel(dc, &dc->blend); if (update_blend_seq) tegra_dc_blend_sequential(dc, &dc->blend); for (i = 0; i < DC_N_WINDOWS; i++) { if (!no_vsync) dc->windows[i].dirty = 1; update_mask |= WIN_A_ACT_REQ << i; } } tegra_dc_set_dynamic_emc(windows, n); tegra_dc_writel(dc, update_mask << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, FRAME_END_INT | V_BLANK_INT, DC_CMD_INT_STATUS); if (!no_vsync) { set_bit(V_BLANK_FLIP, &dc->vblank_ref_count); tegra_dc_unmask_interrupt(dc, FRAME_END_INT | V_BLANK_INT | ALL_UF_INT); #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC) set_bit(V_PULSE2_FLIP, &dc->vpulse2_ref_count); tegra_dc_unmask_interrupt(dc, V_PULSE2_INT); #endif } else { clear_bit(V_BLANK_FLIP, &dc->vblank_ref_count); tegra_dc_mask_interrupt(dc, V_BLANK_INT | ALL_UF_INT); #if !defined(CONFIG_ARCH_TEGRA_2x_SOC) && !defined(CONFIG_ARCH_TEGRA_3x_SOC) clear_bit(V_PULSE2_FLIP, &dc->vpulse2_ref_count); tegra_dc_mask_interrupt(dc, V_PULSE2_INT); #endif if (!atomic_read(&frame_end_ref)) tegra_dc_mask_interrupt(dc, FRAME_END_INT); } if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) schedule_delayed_work(&dc->one_shot_work, msecs_to_jiffies(dc->one_shot_delay_ms)); /* update EMC clock if calculated bandwidth has changed */ tegra_dc_program_bandwidth(dc, false); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) update_mask |= NC_HOST_TRIG; tegra_dc_writel(dc, update_mask, DC_CMD_STATE_CONTROL); tegra_dc_release_dc_out(dc); /* tegra_dc_io_end() is called in tegra_dc_sync_windows() */ mutex_unlock(&dc->lock); if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) mutex_unlock(&dc->one_shot_lock); return 0; }
/* Functional initialization */ void nvsd_init(struct tegra_dc *dc, struct tegra_dc_sd_settings *settings) { u32 i = 0; u32 val = 0; u32 bw = 0; u32 bw_idx = 0; /* TODO: check if HW says SD's available */ tegra_dc_io_start(dc); /* If SD's not present or disabled, clear the register and return. */ if (!settings || settings->enable == 0) { /* clear the brightness val, too. */ if (_sd_brightness) atomic_set(_sd_brightness, 255); _sd_brightness = NULL; if (settings) settings->phase_settings_step = 0; tegra_dc_writel(dc, 0, DC_DISP_SD_CONTROL); tegra_dc_io_end(dc); return; } dev_dbg(&dc->ndev->dev, "NVSD Init:\n"); /* init agg_priorities */ if (!settings->agg_priorities.agg[0]) settings->agg_priorities.agg[0] = settings->aggressiveness; /* WAR: Settings will not be valid until the next flip. * Thus, set manual K to either HW's current value (if * we're already enabled) or a non-effective value (if * we're about to enable). */ val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); if (val & SD_ENABLE_NORMAL) if (settings->phase_in_adjustments) i = tegra_dc_readl(dc, DC_DISP_SD_MAN_K_VALUES); else i = tegra_dc_readl(dc, DC_DISP_SD_HW_K_VALUES); else i = 0; /* 0 values for RGB = 1.0, i.e. non-affected */ tegra_dc_writel(dc, i, DC_DISP_SD_MAN_K_VALUES); /* Enable manual correction mode here so that changing the * settings won't immediately impact display dehavior. */ val |= SD_CORRECTION_MODE_MAN; tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); bw_idx = nvsd_get_bw_idx(settings); bw = SD_BIN_WIDTH(bw_idx); /* Values of SD LUT & BL TF are different according to bin_width on T30 * due to HW bug. Therefore we use bin_width to select the correct table * on T30. On T114, we will use 1st table by default.*/ #ifdef CONFIG_TEGRA_SD_GEN2 bw_idx = 0; #endif /* Write LUT */ if (!settings->cmd) { dev_dbg(&dc->ndev->dev, " LUT:\n"); for (i = 0; i < DC_DISP_SD_LUT_NUM; i++) { val = SD_LUT_R(settings->lut[bw_idx][i].r) | SD_LUT_G(settings->lut[bw_idx][i].g) | SD_LUT_B(settings->lut[bw_idx][i].b); tegra_dc_writel(dc, val, DC_DISP_SD_LUT(i)); dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); } } /* Write BL TF */ if (!settings->cmd) { dev_dbg(&dc->ndev->dev, " BL_TF:\n"); for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { val = SD_BL_TF_POINT_0(settings->bltf[bw_idx][i][0]) | SD_BL_TF_POINT_1(settings->bltf[bw_idx][i][1]) | SD_BL_TF_POINT_2(settings->bltf[bw_idx][i][2]) | SD_BL_TF_POINT_3(settings->bltf[bw_idx][i][3]); tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); } } else if ((settings->cmd & PHASE_IN)) { settings->cmd &= ~PHASE_IN; /* Write NO_OP values for BLTF */ for (i = 0; i < DC_DISP_SD_BL_TF_NUM; i++) { val = SD_BL_TF_POINT_0(0xFF) | SD_BL_TF_POINT_1(0xFF) | SD_BL_TF_POINT_2(0xFF) | SD_BL_TF_POINT_3(0xFF); tegra_dc_writel(dc, val, DC_DISP_SD_BL_TF(i)); dev_dbg(&dc->ndev->dev, " %d: 0x%08x\n", i, val); } } /* Set step correctly on init */ if (!settings->cmd && settings->phase_in_settings) { settings->num_phase_in_steps = STEPS_PER_AGG_LVL * settings->aggressiveness; settings->phase_settings_step = settings->enable ? settings->num_phase_in_steps : 0; } /* Write Coeff */ val = SD_CSC_COEFF_R(settings->coeff.r) | SD_CSC_COEFF_G(settings->coeff.g) | SD_CSC_COEFF_B(settings->coeff.b); tegra_dc_writel(dc, val, DC_DISP_SD_CSC_COEFF); dev_dbg(&dc->ndev->dev, " COEFF: 0x%08x\n", val); /* Write BL Params */ val = SD_BLP_TIME_CONSTANT(settings->blp.time_constant) | SD_BLP_STEP(settings->blp.step); tegra_dc_writel(dc, val, DC_DISP_SD_BL_PARAMETERS); dev_dbg(&dc->ndev->dev, " BLP: 0x%08x\n", val); /* Write Auto/Manual PWM */ val = (settings->use_auto_pwm) ? SD_BLC_MODE_AUTO : SD_BLC_MODE_MAN; tegra_dc_writel(dc, val, DC_DISP_SD_BL_CONTROL); dev_dbg(&dc->ndev->dev, " BL_CONTROL: 0x%08x\n", val); /* Write Flicker Control */ val = SD_FC_TIME_LIMIT(settings->fc.time_limit) | SD_FC_THRESHOLD(settings->fc.threshold); tegra_dc_writel(dc, val, DC_DISP_SD_FLICKER_CONTROL); dev_dbg(&dc->ndev->dev, " FLICKER_CONTROL: 0x%08x\n", val); #ifdef CONFIG_TEGRA_SD_GEN2 /* Write K limit */ if (settings->k_limit_enable) { val = settings->k_limit; if (val < 128) val = 128; else if (val > 255) val = 255; val = SD_K_LIMIT(val); tegra_dc_writel(dc, val, DC_DISP_SD_K_LIMIT); dev_dbg(&dc->ndev->dev, " K_LIMIT: 0x%08x\n", val); } if (settings->sd_window_enable) { /* Write sd window */ val = SD_WIN_H_POSITION(settings->sd_window.h_position) | SD_WIN_V_POSITION(settings->sd_window.v_position); tegra_dc_writel(dc, val, DC_DISP_SD_WINDOW_POSITION); dev_dbg(&dc->ndev->dev, " SD_WINDOW_POSITION: 0x%08x\n", val); val = SD_WIN_H_POSITION(settings->sd_window.h_size) | SD_WIN_V_POSITION(settings->sd_window.v_size); tegra_dc_writel(dc, val, DC_DISP_SD_WINDOW_SIZE); dev_dbg(&dc->ndev->dev, " SD_WINDOW_SIZE: 0x%08x\n", val); } if (settings->soft_clipping_enable) { /* Write soft clipping */ val = (64 * 1024) / (256 - settings->soft_clipping_threshold); val = SD_SOFT_CLIPPING_RECIP(val) | SD_SOFT_CLIPPING_THRESHOLD(settings->soft_clipping_threshold); tegra_dc_writel(dc, val, DC_DISP_SD_SOFT_CLIPPING); dev_dbg(&dc->ndev->dev, " SOFT_CLIPPING: 0x%08x\n", val); } if (settings->smooth_k_enable) { fixed20_12 smooth_k_incr; fixed20_12 num; /* Write K incr value */ val = SD_SMOOTH_K_INCR(settings->smooth_k_incr); tegra_dc_writel(dc, val, DC_DISP_SD_SMOOTH_K); dev_dbg(&dc->ndev->dev, " SMOOTH_K: 0x%08x\n", val); /* Convert 8.6 fixed-point to 20.12 fixed-point */ smooth_k_incr.full = val << 6; /* In the BL_TF LUT, raw K is specified in steps of 8 */ num.full = dfixed_const(8); num.full = dfixed_div(num, smooth_k_incr); num.full = dfixed_ceil(num); smooth_k_frames_left = dfixed_trunc(num); smooth_k_duration_frames = smooth_k_frames_left; dev_dbg(&dc->ndev->dev, " Smooth K duration (frames): %d\n", smooth_k_frames_left); } #endif /* Manage SD Control */ val = 0; /* Stay in manual correction mode until the next flip. */ val |= SD_CORRECTION_MODE_MAN; /* Enable / One-Shot */ val |= (settings->enable == 2) ? (SD_ENABLE_ONESHOT | SD_ONESHOT_ENABLE) : SD_ENABLE_NORMAL; /* HW Update Delay */ val |= SD_HW_UPDATE_DLY(settings->hw_update_delay); /* Video Luma */ val |= (settings->use_vid_luma) ? SD_USE_VID_LUMA : 0; /* Aggressiveness */ val |= SD_AGGRESSIVENESS(settings->aggressiveness); /* Bin Width (value derived above) */ val |= bw; #ifdef CONFIG_TEGRA_SD_GEN2 /* K limit enable */ val |= (settings->k_limit_enable) ? SD_K_LIMIT_ENABLE : 0; /* Programmable sd window enable */ val |= (settings->sd_window_enable) ? SD_WINDOW_ENABLE : 0; /* Soft clipping enable */ val |= (settings->soft_clipping_enable) ? SD_SOFT_CLIPPING_ENABLE : 0; /* Smooth K enable */ val |= (settings->smooth_k_enable) ? SD_SMOOTH_K_ENABLE : 0; /* SD proc control */ val |= (settings->use_vpulse2) ? SD_VPULSE2 : SD_VSYNC; #endif /* Finally, Write SD Control */ tegra_dc_writel(dc, val, DC_DISP_SD_CONTROL); dev_dbg(&dc->ndev->dev, " SD_CONTROL: 0x%08x\n", val); tegra_dc_io_end(dc); /* set the brightness pointer */ _sd_brightness = settings->sd_brightness; /* note that we're in manual K until the next flip */ atomic_set(&man_k_until_blank, 1); }
static void tegra_dc_rgb_enable(struct tegra_dc *dc) { int i; u32 out_sel_pintable[ARRAY_SIZE(tegra_dc_rgb_enable_out_sel_pintable)]; tegra_dc_io_start(dc); tegra_dc_writel(dc, PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE | PW4_ENABLE | PM0_ENABLE | PM1_ENABLE, DC_CMD_DISPLAY_POWER_CONTROL); tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); if (dc->out->out_pins) { tegra_dc_set_out_pin_polars(dc, dc->out->out_pins, dc->out->n_out_pins); tegra_dc_write_table(dc, tegra_dc_rgb_enable_partial_pintable); } else { tegra_dc_write_table(dc, tegra_dc_rgb_enable_pintable); } memcpy(out_sel_pintable, tegra_dc_rgb_enable_out_sel_pintable, sizeof(tegra_dc_rgb_enable_out_sel_pintable)); /* The display panel sub-board used on FPGA platforms (panel 86) is non-standard. It expects the Data Enable signal on the WR pin instead of the DE pin. */ if (tegra_platform_is_fpga()) out_sel_pintable[3*2+1] = 0x00200000; if (dc->out && dc->out->out_sel_configs) { u8 *out_sels = dc->out->out_sel_configs; for (i = 0; i < dc->out->n_out_sel_configs; i++) { switch (out_sels[i]) { case TEGRA_PIN_OUT_CONFIG_SEL_LM1_M1: out_sel_pintable[5*2+1] = (out_sel_pintable[5*2+1] & ~PIN5_LM1_LCD_M1_OUTPUT_MASK) | PIN5_LM1_LCD_M1_OUTPUT_M1; break; case TEGRA_PIN_OUT_CONFIG_SEL_LM1_LD21: out_sel_pintable[5*2+1] = (out_sel_pintable[5*2+1] & ~PIN5_LM1_LCD_M1_OUTPUT_MASK) | PIN5_LM1_LCD_M1_OUTPUT_LD21; break; case TEGRA_PIN_OUT_CONFIG_SEL_LM1_PM1: out_sel_pintable[5*2+1] = (out_sel_pintable[5*2+1] & ~PIN5_LM1_LCD_M1_OUTPUT_MASK) | PIN5_LM1_LCD_M1_OUTPUT_PM1; break; default: dev_err(&dc->ndev->dev, "Invalid pin config[%d]: %d\n", i, out_sels[i]); break; } } } tegra_dc_write_table(dc, out_sel_pintable); /* Inform DC register updated */ tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); tegra_dc_io_end(dc); }
int esc_mods_tegra_dc_setup_sd(struct file *fp, struct MODS_TEGRA_DC_SETUP_SD *args) { int i; struct tegra_dc *dc = tegra_dc_get_dc(args->head); struct tegra_dc_sd_settings *sd_settings = dc->out->sd_settings; #if defined(CONFIG_ARCH_TEGRA_12x_SOC) u32 val; #endif u32 bw_idx; LOG_ENT(); BUG_ON(args->head > TEGRA_MAX_DC); sd_settings->enable = args->enable ? 1 : 0; sd_settings->use_auto_pwm = false; sd_settings->hw_update_delay = 0; sd_settings->aggressiveness = args->aggressiveness; sd_settings->bin_width = (1 << args->bin_width_log2); sd_settings->phase_in_settings = 0; sd_settings->phase_in_adjustments = 0; sd_settings->cmd = 0; sd_settings->final_agg = args->aggressiveness; sd_settings->cur_agg_step = 0; sd_settings->phase_settings_step = 0; sd_settings->phase_adj_step = 0; sd_settings->num_phase_in_steps = 0; sd_settings->agg_priorities.agg[0] = args->aggressiveness; sd_settings->use_vid_luma = args->use_vid_luma; sd_settings->coeff.r = args->csc_r; sd_settings->coeff.g = args->csc_g; sd_settings->coeff.b = args->csc_b; sd_settings->k_limit_enable = (args->klimit != 0); sd_settings->k_limit = args->klimit; sd_settings->sd_window_enable = true; sd_settings->sd_window.h_position = args->win_x; sd_settings->sd_window.v_position = args->win_y; sd_settings->sd_window.h_size = args->win_w; sd_settings->sd_window.v_size = args->win_h; sd_settings->soft_clipping_enable = true; sd_settings->soft_clipping_threshold = args->soft_clipping_threshold; sd_settings->smooth_k_enable = (args->smooth_k_inc != 0); sd_settings->smooth_k_incr = args->smooth_k_inc; sd_settings->sd_proc_control = false; sd_settings->soft_clipping_correction = false; sd_settings->use_vpulse2 = false; sd_settings->fc.time_limit = 0; sd_settings->fc.threshold = 0; sd_settings->blp.time_constant = 1024; sd_settings->blp.step = 0; #ifdef CONFIG_TEGRA_SD_GEN2 bw_idx = 0; #else bw_idx = args->bin_width_log2; #endif for (i = 0; i < MODS_TEGRA_DC_SETUP_BLTF_SIZE; i++) { sd_settings->bltf[bw_idx][i/4][i%4] = args->bltf[i]; } for (i = 0; i < MODS_TEGRA_DC_SETUP_SD_LUT_SIZE; i++) { sd_settings->lut[bw_idx][i].r = args->lut[i] & 0xff; sd_settings->lut[bw_idx][i].g = (args->lut[i] >> 8) & 0xff; sd_settings->lut[bw_idx][i].b = (args->lut[i] >> 16) & 0xff; } #if defined(CONFIG_TEGRA_NVSD) nvsd_init(dc, sd_settings); #endif #if defined(CONFIG_ARCH_TEGRA_12x_SOC) tegra_dc_io_start(dc); val = tegra_dc_readl(dc, DC_DISP_SD_CONTROL); val &= ~SD_KINIT_BIAS(0); val &= ~SD_CORRECTION_MODE_MAN; tegra_dc_writel(dc, val | SD_KINIT_BIAS(args->k_init_bias), DC_DISP_SD_CONTROL); tegra_dc_io_end(dc); #endif if (dc->enabled) { mutex_lock(&dc->lock); tegra_dc_get(dc); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); tegra_dc_put(dc); mutex_unlock(&dc->lock); } LOG_EXT(); return 0; }