/* * drivers/video/tegra/dc/nvsr.c * * Copyright (c) 2014, NVIDIA CORPORATION, All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include "nvsr.h" #include "nvsr_regs.h" #include "dpaux_regs.h" #define HIMAX 1 #define NVSR_ERR(...) dev_err(&nvsr->dc->ndev->dev, "NVSR: " __VA_ARGS__); #define NVSR_WARN(...) dev_warn(&nvsr->dc->ndev->dev, "NVSR: " __VA_ARGS__); #define NVSR_INFO(...) dev_info(&nvsr->dc->ndev->dev, "NVSR: " __VA_ARGS__); #define NVSR_RET(ret, ...) \ { \ if (ret) { \ NVSR_ERR(__VA_ARGS__); \ return; \ } \ } #define NVSR_RETV(ret, ...) \ { \ if (ret) { \ NVSR_ERR(__VA_ARGS__); \ return ret; \ } \ } /* Wait for 3 frames max to enter/exit */ #define SR_ENTRY_MAX_TRIES 3 #define SR_EXIT_MAX_TRIES 3 static void tegra_dc_nvsr_get(struct tegra_dc_nvsr_data *nvsr) { tegra_dc_get(nvsr->dc); clk_prepare_enable(nvsr->out_clk); } static void tegra_dc_nvsr_put(struct tegra_dc_nvsr_data *nvsr) { clk_disable_unprepare(nvsr->out_clk); tegra_dc_put(nvsr->dc); } static int tegra_dc_nvsr_enter_sr(struct tegra_dc_nvsr_data *nvsr) { u32 val; int ret; u8 tries = 0; u32 delay = nvsr->sr_timing_frame_time_us / 1000; /* Set entry via sideband */ val = NVSR_SRC_CTL1_SIDEBAND_ENTRY_SEL_YES; val |= NVSR_SRC_CTL1_SIDEBAND_ENTRY_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL1, 1, val); NVSR_RETV(ret, "Failed to enter SR via sideband\n"); /* Enable SR */ val = NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED; val |= NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE; /* Request SR entry */ val |= NVSR_SRC_CTL0_SR_ENTRY_REQ_YES; val |= NVSR_SRC_CTL0_SR_ENTRY_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, val); NVSR_RETV(ret, "Failed to request SR entry\n"); /* TODO: Polling is for NVSR bringup only. * Need to implement IRQ */ do { ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val); NVSR_RETV(ret, "Failed to read status register.\n"); if ((val & NVSR_STATUS_SR_STATE_MASK) == NVSR_STATUS_SR_STATE_SR_ACTIVE) { nvsr->sr_active = true; return 0; } msleep(delay); } while (++tries < SR_ENTRY_MAX_TRIES); NVSR_ERR("Timed out while waiting for SR entry\n"); return -ETIMEDOUT; } static int tegra_dc_nvsr_exit_sr(struct tegra_dc_nvsr_data *nvsr) { u32 val; int ret; u8 tries = 0; /* Poll every quarter frame in ms */ u32 delay = nvsr->sr_timing_frame_time_us / 1000; switch (nvsr->resync_method) { case NVSR_RESYNC_CTL_METHOD_FL: val = NVSR_RESYNC_CTL_METHOD_FL; val |= nvsr->resync_delay << NVSR_RESYNC_CTL_DELAY_SHIFT; break; case NVSR_RESYNC_CTL_METHOD_SS: case NVSR_RESYNC_CTL_METHOD_BS: NVSR_WARN("Sliding Sync and Blank Stretching resync methods not supported; defaulting to Immediate\n"); case NVSR_RESYNC_CTL_METHOD_IMM: default: val = NVSR_RESYNC_CTL_METHOD_IMM; break; } ret = nvsr->reg_ops.write(nvsr, NVSR_RESYNC_CTL, 1, val); NVSR_RETV(ret, "Couldn't write resync method, aborting SR exit\n"); val = NVSR_SRC_CTL1_SIDEBAND_EXIT_SEL_YES; val |= NVSR_SRC_CTL1_SIDEBAND_EXIT_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL1, 1, val); NVSR_RETV(ret, "Couldn't select sideband exit, aborting SR exit\n"); /* Request SR exit */ val = NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE; val |= NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED; val |= NVSR_SRC_CTL0_SR_EXIT_REQ_YES; val |= NVSR_SRC_CTL0_SR_EXIT_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, val); NVSR_RETV(ret, "Couldn't request SR exit, aborting SR exit\n"); /* TODO: Polling is for NVSR bringup only. * Need to implement IRQ */ do { ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val); NVSR_RETV(ret, "Failed to read status register.\n"); if ((val & NVSR_STATUS_SR_STATE_MASK) == NVSR_STATUS_SR_STATE_IDLE) { nvsr->sr_active = false; return 0; } msleep(delay); } while (++tries < SR_EXIT_MAX_TRIES); return -ETIMEDOUT; } static int tegra_dc_nvsr_src_power_on(struct tegra_dc_nvsr_data *nvsr) { int ret; u32 reg_val = 0; u8 tries = 0; ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, ®_val); if (ret) { NVSR_ERR("Failed to read status register.\n"); return ret; } /* If SRC is on, we're done */ if ((reg_val & NVSR_STATUS_SR_STATE_MASK) == NVSR_STATUS_SR_STATE_IDLE) return 0; reg_val = NVSR_SRC_CTL0_SR_ENABLE_CTL_ENABLED; reg_val |= NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, reg_val); NVSR_RETV(ret, "Failed to write to control register.\n"); /* TODO: Polling is for NVSR bringup only. * Need to implement IRQ */ do { ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, ®_val); NVSR_RETV(ret, "Failed to read status register.\n"); if ((reg_val & NVSR_STATUS_SR_STATE_MASK) == NVSR_STATUS_SR_STATE_IDLE) { nvsr->src_on = true; return 0; } msleep(nvsr->sr_timing_frame_time_us/1000); } while (tries++ < 3); NVSR_ERR("Failed to power on the SRC.\n"); return -ETIMEDOUT; } static int tegra_dc_nvsr_src_power_off(struct tegra_dc_nvsr_data *nvsr) { int ret; u32 reg_val = 0; u8 tries = 0; ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, ®_val); if (ret) { NVSR_ERR("Failed to read status register.\n"); return ret; } if ((reg_val & NVSR_STATUS_SR_STATE_MASK) == NVSR_STATUS_SR_STATE_OFFLINE) return 0; reg_val = NVSR_SRC_CTL0_SR_ENABLE_CTL_DISABLED; reg_val |= NVSR_SRC_CTL0_SR_ENABLE_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_SRC_CTL0, 1, reg_val); NVSR_RETV(ret, "Failed to write to control register.\n"); /* TODO: Polling is for NVSR bringup only. * Need to implement IRQ */ do { ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, ®_val); NVSR_RETV(ret, "Failed to read status register.\n"); if ((reg_val & NVSR_STATUS_SR_STATE_MASK) == NVSR_STATUS_SR_STATE_OFFLINE) { nvsr->src_on = false; /* Will need to re-init SRC on next power-on */ nvsr->is_init = false; nvsr->sr_active = false; nvsr->idle_active = false; return 0; } msleep(nvsr->sr_timing_frame_time_us/1000); } while (tries++ < 3); NVSR_ERR("Failed to power off the SRC.\n"); return -ETIMEDOUT; } /* Read SRC capabilities and assess possible refresh modes. */ static int tegra_dc_nvsr_query_capabilities(struct tegra_dc_nvsr_data *nvsr) { u32 reg_val; int ret; ret = nvsr->reg_ops.read(nvsr, NVSR_SR_CAPS0, 1, ®_val); NVSR_RETV(ret, "Failed to read cap reg 0.\n"); nvsr->cap.sr = reg_val & NVSR_SR_CAPS0_SR_CAPABLE_MASK >> NVSR_SR_CAPS0_SR_CAPABLE_SHIFT; nvsr->cap.sr_entry_req = reg_val & NVSR_SR_CAPS0_SR_ENTRY_REQ_MASK >> NVSR_SR_CAPS0_SR_ENTRY_REQ_SHIFT; nvsr->cap.sr_exit_req = reg_val & NVSR_SR_CAPS0_SR_EXIT_REQ_MASK >> NVSR_SR_CAPS0_SR_EXIT_REQ_SHIFT; if (!nvsr->cap.sr || !nvsr->cap.sr_entry_req || !nvsr->cap.sr_exit_req) { NVSR_ERR("SRC can't support self-refresh.\n"); return 0; } nvsr->cap.resync = reg_val & NVSR_SR_CAPS0_RESYNC_CAP_MASK >> NVSR_SR_CAPS0_RESYNC_CAP_SHIFT; nvsr->cap.sparse_mode_support = true; /* Parse device/vendor ID */ ret = nvsr->reg_ops.read(nvsr, NVSR_SRC_ID0, 4, ®_val); NVSR_RETV(ret, "Failed to read device ID.\n"); nvsr->src_id.device_id = reg_val & 0xffff; nvsr->src_id.vendor_id = reg_val >> 16; ret = nvsr->reg_ops.read(nvsr, NVSR_SR_CAPS2, 1, ®_val); NVSR_RETV(ret, "Failed to read capabilities register 2.\n"); if ((reg_val & NVSR_SR_CAPS2_SEPERATE_PCLK_SUPPORTED) && (reg_val & NVSR_SR_CAPS2_BUFFERED_REFRESH_SUPPORTED)) { u32 max_pclk = 0; ret = nvsr->reg_ops.read(nvsr, NVSR_SR_MAX_PCLK0, 2, &max_pclk); if (ret) NVSR_WARN("Failed to read SRC input max pclk\n"); if (max_pclk > 0) { /* NVSR stores SRC input max pclk in units of 20KHz */ nvsr->cap.max_pt_pclk = max_pclk * 20000; nvsr->cap.sep_pclk = true; nvsr->cap.buffered_mode_support = reg_val & NVSR_SR_CAPS2_FRAME_LOCK_MASK >> NVSR_SR_CAPS2_FRAME_LOCK_SHIFT; nvsr->cap.burst_mode_support = nvsr->cap.resync == NVSR_SR_CAPS0_RESYNC_CAP_FL; } else NVSR_WARN("SRC input max pclk is 0.\n"); } nvsr->cap.is_init = true; return 0; } /* Write self-refresh timing: SRC to panel */ static int tegra_dc_nvsr_write_sr_timing(struct tegra_dc_nvsr_data *nvsr) { int ret; struct tegra_dc_mode *mode = &nvsr->sr_timing; u32 val, hblank, vblank, pclk; /* NVSR stores pclk in units of 20KHz */ val = DIV_ROUND_UP(mode->pclk, 20000); ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_PIXEL_CLOCK0, 2, val); NVSR_RETV(ret, "Failed to write SR pclk\n"); pclk = val * 20000; /* Horizontal timing */ ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING0, 2, mode->h_active); NVSR_RETV(ret, "Failed to write SR Hactive\n"); val = mode->h_front_porch + mode->h_sync_width + mode->h_back_porch; ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING2, 2, val); NVSR_RETV(ret, "Failed to write SR Hblank\n"); hblank = val; ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING4, 2, mode->h_front_porch); NVSR_RETV(ret, "Failed to write SR Hfp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING6, 2, mode->h_back_porch); NVSR_RETV(ret, "Failed to write SR Hbp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING8, 1, mode->h_sync_width); NVSR_RETV(ret, "Failed to write SR Hsync width\n"); val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) ? NVSR_SRMODE_TIMING9_HSYNC_POL_NEG : NVSR_SRMODE_TIMING9_HSYNC_POL_POS; ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING9, 1, val); NVSR_RETV(ret, "Failed to write SR Hsync pulse polarity\n"); /* Vertical timing */ ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING10, 2, mode->v_active); NVSR_RETV(ret, "Failed to write SR Vactive\n"); val = mode->v_front_porch + mode->v_sync_width + mode->v_back_porch; ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING12, 2, val); NVSR_RETV(ret, "Failed to write SR Vblank\n"); vblank = val; ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING14, 2, mode->v_front_porch); NVSR_RETV(ret, "Failed to write SR Vfp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING16, 2, mode->v_back_porch); NVSR_RETV(ret, "Failed to write SR Vbp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING18, 1, mode->v_sync_width); NVSR_RETV(ret, "Failed to write SR Vsync width\n"); val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) ? NVSR_SRMODE_TIMING19_VSYNC_POL_NEG : NVSR_SRMODE_TIMING19_VSYNC_POL_POS; ret = nvsr->reg_ops.write(nvsr, NVSR_SRMODE_TIMING19, 1, val); NVSR_RETV(ret, "Failed to write SR Hsync pulse polarity\n"); nvsr->sr_timing_frame_time_us = pclk / (mode->h_active + hblank) / (mode->v_active + vblank); nvsr->sr_timing_frame_time_us = 1000000 / nvsr->sr_timing_frame_time_us; return 0; } /* Write pass-through timing: DC to SRC */ static int tegra_dc_nvsr_write_pt_timing(struct tegra_dc_nvsr_data *nvsr) { int ret; struct tegra_dc_mode *mode = &nvsr->pt_timing; u32 val, hblank, vblank, pclk; if (mode->pclk > nvsr->cap.max_pt_pclk) { NVSR_ERR("DC->SRC pixel clock (%dHz) > max (%dHz)\n", mode->pclk, nvsr->cap.max_pt_pclk); return -EINVAL; } /* NVSR stores pclk in units of 20KHz */ val = DIV_ROUND_UP(mode->pclk, 20000); ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_PIXEL_CLOCK0, 2, val); NVSR_RETV(ret, "Failed to write PT pclk\n"); pclk = val * 20000; /* Horizontal timing */ ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING0, 2, mode->h_active); NVSR_RETV(ret, "Failed to write PT Hactive\n"); val = mode->h_front_porch + mode->h_sync_width + mode->h_back_porch; ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING2, 2, val); NVSR_RETV(ret, "Failed to write PT Hblank\n"); hblank = val; ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING4, 2, mode->h_front_porch); NVSR_RETV(ret, "Failed to write PT Hfp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING6, 2, mode->h_back_porch); NVSR_RETV(ret, "Failed to write PT Hbp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING8, 1, mode->h_sync_width); NVSR_RETV(ret, "Failed to write PT Hsync width\n"); val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_H_SYNC) ? NVSR_PTMODE_TIMING9_HSYNC_POL_NEG : NVSR_PTMODE_TIMING9_HSYNC_POL_POS; ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING9, 1, val); NVSR_RETV(ret, "Failed to write PT Hsync pulse polarity\n"); /* Vertical timing */ ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING10, 2, mode->v_active); NVSR_RETV(ret, "Failed to write PT Vactive\n"); val = mode->v_front_porch + mode->v_sync_width + mode->v_back_porch; ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING12, 2, val); NVSR_RETV(ret, "Failed to write PT Vblank\n"); vblank = val; ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING14, 2, mode->v_front_porch); NVSR_RETV(ret, "Failed to write PT Vfp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING16, 2, mode->v_back_porch); NVSR_RETV(ret, "Failed to write PT Vbp\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING18, 1, mode->v_sync_width); NVSR_RETV(ret, "Failed to write PT Vsync width\n"); val = (mode->flags & TEGRA_DC_MODE_FLAG_NEG_V_SYNC) ? NVSR_PTMODE_TIMING19_VSYNC_POL_NEG : NVSR_PTMODE_TIMING19_VSYNC_POL_POS; ret = nvsr->reg_ops.write(nvsr, NVSR_PTMODE_TIMING19, 1, val); NVSR_RETV(ret, "Failed to write PT Hsync pulse polarity\n"); nvsr->pt_timing_frame_time_us = pclk / (mode->h_active + hblank) / (mode->v_active + vblank); nvsr->pt_timing_frame_time_us = 1000000 / nvsr->pt_timing_frame_time_us; return 0; } static int tegra_dc_nvsr_update_timings(struct tegra_dc_nvsr_data *nvsr) { int ret, i; struct tegra_dc_mode *modes = nvsr->dc->out->modes; struct tegra_dc_mode *mode = &nvsr->dc->mode; int n_modes = nvsr->dc->out->n_modes; u32 min_vblank, max_vblank, min_hblank, max_hblank; u32 vblank, hblank; /* If SR isn't supported, no need to program timings */ if (!nvsr->cap.sparse_mode_support) return 0; /* Find min/max blank timings */ if (n_modes) { min_vblank = max_vblank = modes[0].v_front_porch + modes[0].v_sync_width + modes[0].v_back_porch; min_hblank = max_hblank = modes[0].h_front_porch + modes[0].h_sync_width + modes[0].h_back_porch; for (i = 1; i < n_modes; i++) { vblank = modes[i].v_front_porch + modes[i].v_sync_width + modes[i].v_back_porch; hblank = modes[i].h_front_porch + modes[i].h_sync_width + modes[i].h_back_porch; if (vblank > max_vblank) max_vblank = vblank; if (vblank < min_vblank) min_vblank = vblank; if (hblank > max_hblank) max_hblank = hblank; if (hblank < min_hblank) min_hblank = hblank; } } else { max_vblank = min_vblank = mode->v_front_porch + mode->v_sync_width + mode->v_back_porch; max_hblank = min_hblank = mode->h_front_porch + mode->h_sync_width + mode->h_back_porch; } ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING0, 2, min_vblank); NVSR_RETV(ret, "Failed to init timing\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING2, 2, max_vblank); NVSR_RETV(ret, "Failed to init timing\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING4, 2, min_hblank); NVSR_RETV(ret, "Failed to init timing\n"); ret = nvsr->reg_ops.write(nvsr, NVSR_BLANK_TIMING6, 2, max_hblank); NVSR_RETV(ret, "Failed to init timing\n"); ret = tegra_dc_nvsr_write_pt_timing(nvsr); NVSR_RETV(ret, "Failed to init pass-through timing\n"); ret = tegra_dc_nvsr_write_sr_timing(nvsr); NVSR_RETV(ret, "Failed to init self-refresh timing\n"); return 0; } static int tegra_nvsr_read_dpaux(struct tegra_dc_nvsr_data *nvsr, u32 reg, u32 size, u32 *val) { int ret, i; u32 aux_stat; u8 data[DP_AUX_MAX_BYTES] = {0}; ret = tegra_dc_dpaux_read(nvsr->out_data.dp, DPAUX_DP_AUXCTL_CMD_AUXRD, reg, data, &size, &aux_stat); NVSR_RETV(ret, "DPAUX read failed: reg = 0x%x, size = %d, aux_stat = %d\n", reg, size, aux_stat); *val = 0; for (i = 0; i < DP_AUX_MAX_BYTES; i++) *val |= data[i] << (i * 8); return 0; } static int tegra_nvsr_write_dpaux(struct tegra_dc_nvsr_data *nvsr, u32 reg, u32 size, u64 val) { int i, ret; u32 aux_stat; u8 data[DP_AUX_MAX_BYTES] = {0}; for (i = 0; i < size; i++) { data[i] = val & 0xffllu; val = val >> 8; } /* HACK: Himax tcon always reports failure when writing; * write 1 byte at a time to avoid breaking out of DP * write code too early */ ret = tegra_dc_dpaux_write(nvsr->out_data.dp, DPAUX_DP_AUXCTL_CMD_AUXWR, reg, data, &size, &aux_stat); /* Current HIMAX silicon always NACKs dpaux writes */ #if !HIMAX if (ret) return ret; #endif return 0; } /* Enter Sparse and turn off link, or single frame update */ static int tegra_dc_nvsr_enter_idle(struct tegra_dc_nvsr_data *nvsr) { int ret = 0; struct tegra_dc *dc = nvsr->dc; if (!nvsr->is_init || !nvsr->src_on || !nvsr->enable || !nvsr->cap.sparse_mode_support) return -ENODEV; if (!nvsr->idle_active) { /* Start a new self-refresh state */ ret = tegra_dc_nvsr_enter_sr(nvsr); NVSR_RETV(ret, "Entering SR failed\n"); /* TODO: Turn off link here */ /* set non-continuous mode */ tegra_dc_writel(dc, DISP_CTRL_MODE_NC_DISPLAY, DC_CMD_DISPLAY_COMMAND); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); dc->out->flags |= TEGRA_DC_OUT_ONE_SHOT_MODE; nvsr->idle_active = true; } else { tegra_dc_nvsr_get(nvsr); /* Update cached frame */ switch (nvsr->sr_mode) { case SR_MODE_BURST: /* TODO: Implement Burst Refresh update */ NVSR_ERR("Burst mode unimplemented\n"); break; default: NVSR_ERR("SR re-entry not supported for current refresh mode.\n"); ret = -ENOSYS; } tegra_dc_nvsr_put(nvsr); } return ret; } inline void tegra_dc_nvsr_irq(struct tegra_dc_nvsr_data *nvsr, unsigned long status) { if ((status & MSF_INT) && (nvsr->waiting_on_framelock)) { tegra_dc_mask_interrupt(nvsr->dc, MSF_INT); nvsr->waiting_on_framelock = false; complete(&nvsr->framelock_comp); } } static inline int tegra_dc_nvsr_reset_src(struct tegra_dc_nvsr_data *nvsr) { int ret; ret = tegra_dc_nvsr_src_power_off(nvsr); NVSR_RETV(ret, "Failed to reset SRC.\n"); ret = tegra_dc_nvsr_src_power_on(nvsr); NVSR_RETV(ret, "Failed to reset SRC.\n"); return 0; } static inline void tegra_dc_nvsr_config_resync_method (struct tegra_dc_nvsr_data *nvsr) { u32 val; struct tegra_dc *dc = nvsr->dc; if (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_FL) { nvsr->resync_method = NVSR_RESYNC_CTL_METHOD_FL; /* Configure MSF pin */ val = PIN_OUTPUT_LSPI_OUTPUT_DIS; tegra_dc_writel(dc, val, DC_COM_PIN_OUTPUT_ENABLE3); val = MSF_ENABLE | MSF_LSPI | MSF_POLARITY_LOW; tegra_dc_writel(dc, val, DC_CMD_DISPLAY_COMMAND_OPTION0); /* Enable MSF (framelock) interrupt in DC */ val = tegra_dc_readl(dc, DC_CMD_INT_ENABLE); val |= MSF_INT; tegra_dc_writel(dc, val, DC_CMD_INT_ENABLE); } else if ((nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_BS) || (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_SS)) { NVSR_WARN("Sliding Sync and Blank Stretching resync methods not supported; defaulting to Immediate\n"); nvsr->resync_method = NVSR_RESYNC_CTL_METHOD_IMM; } else nvsr->resync_method = NVSR_RESYNC_CTL_METHOD_IMM; nvsr->resync_delay = 0; } static int tegra_dc_nvsr_init_src(struct tegra_dc_nvsr_data *nvsr) { int ret; u32 val; /* Reset SRC if it's not in idle */ ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val); NVSR_RETV(ret, "Failed to read status register.\n"); if ((val & NVSR_STATUS_SR_STATE_MASK) != NVSR_STATUS_SR_STATE_IDLE) { /* Reset SRC */ ret = tegra_dc_nvsr_reset_src(nvsr); NVSR_RETV(ret, "Failed to reset SRC\n"); } /* Unmask and enable interrupts from SRC */ ret = nvsr->reg_ops.write(nvsr, NVSR_INTR0, 1, 0xff); NVSR_RETV(ret, "Failed to write to interrupt mask regiser.\n"); val = NVSR_INTR1_EN_YES; val |= NVSR_INTR1_EN_MASK_ENABLE; ret = nvsr->reg_ops.write(nvsr, NVSR_INTR1, 1, val); NVSR_RETV(ret, "Failed to write to interrupt enable regiser.\n"); /* Can't populate capabilities during init since DP interface * isn't enabled yet at that point. Read capabilities here instead, * just once */ if (unlikely(!nvsr->cap.is_init)) { ret = tegra_dc_nvsr_query_capabilities(nvsr); NVSR_RETV(ret, "Can't ready capabilities\n"); nvsr->pt_timing = nvsr->dc->mode; nvsr->sr_timing = nvsr->dc->mode; } ret = tegra_dc_nvsr_update_timings(nvsr); NVSR_RETV(ret, "Failed to write SRC timings\n"); tegra_dc_nvsr_config_resync_method(nvsr); nvsr->is_init = true; return 0; } static int tegra_dc_nvsr_exit_idle(struct tegra_dc_nvsr_data *nvsr) { int ret; struct tegra_dc *dc = nvsr->dc; u32 exit_timeout = nvsr->sr_timing_frame_time_us / 1000 * SR_EXIT_MAX_TRIES; if (!nvsr->is_init || !nvsr->src_on || !nvsr->enable) return -ENODEV; if (!nvsr->idle_active) return 0; tegra_dc_get(dc); /* Program current frame as NC, to be triggered in MSF/FL interrupt */ tegra_dc_writel(dc, GENERAL_ACT_REQ | NC_HOST_TRIG, DC_CMD_STATE_CONTROL); /* Set up for FL IRQ, request SR-exit from SRC, and wait */ nvsr->waiting_on_framelock = true; tegra_dc_unmask_interrupt(dc, MSF_INT); ret = tegra_dc_nvsr_exit_sr(nvsr); if (ret) { NVSR_ERR("Exiting SR failed\n"); nvsr->waiting_on_framelock = false; tegra_dc_mask_interrupt(dc, MSF_INT); /* Attempt to reset and re-initialize SRC. */ BUG_ON(tegra_dc_nvsr_init_src(nvsr)); tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); dc->out->flags &= ~TEGRA_DC_OUT_ONE_SHOT_MODE; return ret; } wait_for_completion_interruptible_timeout(&nvsr->framelock_comp, msecs_to_jiffies(exit_timeout)); /* Switch DC NC-->C during the single NC frame */ tegra_dc_writel(dc, DISP_CTRL_MODE_C_DISPLAY, DC_CMD_DISPLAY_COMMAND); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); dc->out->flags &= ~TEGRA_DC_OUT_ONE_SHOT_MODE; init_completion(&nvsr->framelock_comp); tegra_dc_put(dc); nvsr->idle_active = false; return 0; } static int tegra_dc_nvsr_enable_nvsr(struct tegra_dc_nvsr_data *nvsr) { int ret; if (nvsr->enable) return 0; /* tegra_dc_nvsr_init_src will turn * on SRC and do initial config */ ret = tegra_dc_nvsr_init_src(nvsr); NVSR_RETV(ret, "Failed to init SRC\n"); nvsr->enable = true; return 0; } static int tegra_dc_nvsr_disable_nvsr(struct tegra_dc_nvsr_data *nvsr) { int ret; if (!nvsr->enable) return 0; if (nvsr->idle_active) { ret = tegra_dc_nvsr_exit_idle(nvsr); NVSR_RETV(ret, "Can't exit idle, leaving NVSR enabled\n"); } ret = tegra_dc_nvsr_src_power_off(nvsr); NVSR_RETV(ret, "Failed to shut off SRC\n"); nvsr->enable = false; return 0; } static void tegra_dc_nvsr_destroy(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.destroy) nvsr->out_ops.destroy(dc); } static bool tegra_dc_nvsr_detect(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.detect) return nvsr->out_ops.detect(dc); return -EINVAL; } static void tegra_dc_nvsr_enable(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.enable) nvsr->out_ops.enable(dc); tegra_dc_nvsr_init_src(nvsr); } static void tegra_dc_nvsr_disable(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; tegra_dc_nvsr_src_power_off(nvsr); if (nvsr->out_ops.disable) nvsr->out_ops.disable(dc); } static void tegra_dc_nvsr_hold(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.hold) nvsr->out_ops.hold(dc); } static void tegra_dc_nvsr_release(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.release) nvsr->out_ops.release(dc); } static void tegra_dc_nvsr_idle(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.idle) nvsr->out_ops.idle(dc); } static void tegra_dc_nvsr_suspend(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.suspend) nvsr->out_ops.suspend(dc); } static void tegra_dc_nvsr_resume(struct tegra_dc *dc) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.resume) nvsr->out_ops.resume(dc); } static bool tegra_dc_nvsr_mode_filter(const struct tegra_dc *dc, struct fb_videomode *mode) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.mode_filter) return nvsr->out_ops.mode_filter(dc, mode); return -EINVAL; } static long tegra_dc_nvsr_setup_clk(struct tegra_dc *dc, struct clk *clk) { struct tegra_dc_nvsr_data *nvsr = dc->nvsr; if (nvsr->out_ops.setup_clk) return nvsr->out_ops.setup_clk(dc, clk); return -EINVAL; } static void tegra_dc_nvsr_init_out_ops(struct tegra_dc_nvsr_data *nvsr, struct tegra_dc_out_ops *out_ops) { if (out_ops->init) nvsr->out_ops.init = out_ops->init; if (out_ops->destroy) { tegra_dc_nvsr_ops.destroy = tegra_dc_nvsr_destroy; nvsr->out_ops.destroy = out_ops->destroy; } if (out_ops->detect) { tegra_dc_nvsr_ops.detect = tegra_dc_nvsr_detect; nvsr->out_ops.detect = out_ops->detect; } if (out_ops->enable) { tegra_dc_nvsr_ops.enable = tegra_dc_nvsr_enable; nvsr->out_ops.enable = out_ops->enable; } if (out_ops->disable) { tegra_dc_nvsr_ops.disable = tegra_dc_nvsr_disable; nvsr->out_ops.disable = out_ops->disable; } if (out_ops->hold) { tegra_dc_nvsr_ops.hold = tegra_dc_nvsr_hold; nvsr->out_ops.hold = out_ops->hold; } if (out_ops->release) { tegra_dc_nvsr_ops.release = tegra_dc_nvsr_release; nvsr->out_ops.release = out_ops->release; } if (out_ops->idle) { tegra_dc_nvsr_ops.idle = tegra_dc_nvsr_idle; nvsr->out_ops.idle = out_ops->idle; } if (out_ops->suspend) { tegra_dc_nvsr_ops.suspend = tegra_dc_nvsr_suspend; nvsr->out_ops.suspend = out_ops->suspend; } if (out_ops->resume) { tegra_dc_nvsr_ops.resume = tegra_dc_nvsr_resume; nvsr->out_ops.resume = out_ops->resume; } if (out_ops->mode_filter) { tegra_dc_nvsr_ops.mode_filter = tegra_dc_nvsr_mode_filter; nvsr->out_ops.mode_filter = out_ops->mode_filter; } if (out_ops->setup_clk) { tegra_dc_nvsr_ops.setup_clk = tegra_dc_nvsr_setup_clk; nvsr->out_ops.setup_clk = out_ops->setup_clk; } } #ifdef CONFIG_DEBUG_FS static int nvsr_dbg_read_read_show(struct seq_file *s, void *unused) { return 0; } static int nvsr_dbg_read_write_show(struct seq_file *s, void *unused) { return 0; } static int nvsr_dbg_read_open(struct inode *inode, struct file *file) { return single_open(file, nvsr_dbg_read_read_show, inode->i_private); } static int nvsr_dbg_write_open(struct inode *inode, struct file *file) { return single_open(file, nvsr_dbg_read_write_show, inode->i_private); } static const char *tegra_dc_nvsr_state_to_string(u32 val) { val &= NVSR_STATUS_SR_STATE_MASK; switch (val) { case NVSR_STATUS_SR_STATE_OFFLINE: return "Offline"; case NVSR_STATUS_SR_STATE_IDLE: return "Idle"; case NVSR_STATUS_SR_STATE_SR_ENTRY_TRIG: return "SR-entry triggered"; case NVSR_STATUS_SR_STATE_SR_ENTRY_CACHING: return "SR-entry caching"; case NVSR_STATUS_SR_STATE_SR_ENTRY_RDY: return "SR-entry ready"; case NVSR_STATUS_SR_STATE_SR_ACTIVE: return "SR active"; case NVSR_STATUS_SR_STATE_SR_EXIT_TRIG: return "SR-exit triggered"; case NVSR_STATUS_SR_STATE_SR_EXIT_RESYNC: return "SR-exit resynching"; default: return "Unknown"; } /* Should never get here */ return ""; } static int nvsr_dbg_status_show(struct seq_file *s, void *unused) { struct tegra_dc_nvsr_data *nvsr = s->private; u32 val, val2; int ret; if (!nvsr->is_init) { seq_puts(s, "NVSR has not been initialized.\n"); return -EINVAL; } tegra_dc_nvsr_get(nvsr); seq_puts(s, "SRC Info\n"); seq_puts(s, "--------\n"); seq_printf(s, "Device ID: \t\t\t\t%d\n", nvsr->src_id.device_id); seq_printf(s, "Vendor ID: \t\t\t\t%d\n", nvsr->src_id.vendor_id); seq_printf(s, "Sparse Refresh mode supported: \t\t%d\n", nvsr->cap.sparse_mode_support); seq_printf(s, "\tEntry request band capability: \t%d\n", nvsr->cap.sr_entry_req); seq_printf(s, "\tExit request band capability: \t%d\n", nvsr->cap.sr_exit_req); seq_printf(s, "\tResync capabilities:\n"); if (!nvsr->cap.resync) { seq_puts(s, "\t\tImmediate\n"); } else { if (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_FL) seq_puts(s, "\t\tFramelock\n"); if (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_BS) seq_puts(s, "\t\tBlank stretching\n"); if (nvsr->cap.resync & NVSR_SR_CAPS0_RESYNC_CAP_SS) seq_puts(s, "\t\tSliding sync\n"); } seq_printf(s, "Buffered mode supported: \t\t%d\n", nvsr->cap.buffered_mode_support); seq_printf(s, "Burst mode supported: \t\t\t%d\n", nvsr->cap.burst_mode_support); seq_printf(s, "\tSRC max input pclk: \t\t%d\n", nvsr->cap.max_pt_pclk); seq_puts(s, "\n"); seq_puts(s, "SRC status\n"); seq_puts(s, "----------\n"); seq_printf(s, "NVSR functionality enabled: \t%d\n", nvsr->enable); seq_printf(s, "In Sparse Refresh: \t\t%d\n", nvsr->sr_active); seq_printf(s, "Resync method: \t\t\t%s\n", !nvsr->resync_method ? "Immediate" : nvsr->resync_method == NVSR_SR_CAPS0_RESYNC_CAP_FL ? "Framelock" : nvsr->resync_method == NVSR_RESYNC_CTL_METHOD_BS ? "Blank stretching" : nvsr->resync_method == NVSR_SR_CAPS0_RESYNC_CAP_SS ? "Sliding sync" : "unknown"); seq_printf(s, "Resync delay (frames): \t\t%d\n", nvsr->resync_delay); seq_printf(s, "SRC powered: \t\t\t%d\n", nvsr->src_on); ret = nvsr->reg_ops.read(nvsr, NVSR_STATUS, 1, &val); seq_printf(s, "Current SRC status: \t%s\n", tegra_dc_nvsr_state_to_string(val)); seq_puts(s, "PT Timing:\n"); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_PIXEL_CLOCK0, 2, &val); seq_printf(s, "\tPixel clock: \t%dHz\n", ret ? -1 : val * 20000); /* Horizontal PT timing */ ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING0, 2, &val); seq_printf(s, "\tHactive: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING2, 2, &val); seq_printf(s, "\tHblank: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING4, 2, &val); seq_printf(s, "\tHfp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING6, 2, &val); seq_printf(s, "\tHbp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING8, 1, &val); seq_printf(s, "\tHsync width: \t%d\n", val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING9, 1, &val); val2 = val & NVSR_PTMODE_TIMING9_HORZ_BORDER_MASK; val = (val & NVSR_PTMODE_TIMING9_HSYNC_POL_MASK) >> NVSR_PTMODE_TIMING9_HSYNC_POL_SHIFT; seq_printf(s, "\tHborder: \t%d\n", ret ? -1 : val2); seq_printf(s, "\tHsync pulse polarity: \t%d\n", ret ? -1 : val); /* Vertical PT timing */ ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING10, 2, &val); seq_printf(s, "\tVactive: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING12, 2, &val); seq_printf(s, "\tVblank \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING14, 2, &val); seq_printf(s, "\tVfp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING16, 2, &val); seq_printf(s, "\tVbp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING18, 1, &val); seq_printf(s, "\tVsync width: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_PTMODE_TIMING19, 1, &val); val2 = val & NVSR_PTMODE_TIMING19_VERT_BORDER_MASK; val = (val & NVSR_PTMODE_TIMING19_VSYNC_POL_MASK) >> NVSR_PTMODE_TIMING19_VSYNC_POL_SHIFT; seq_printf(s, "\tVborder: \t%d\n", ret ? -1 : val2); seq_printf(s, "\tVsync pulse polarity: \t%d\n", ret ? -1 : val); seq_printf(s, "\tFrame time: \t%dus\n", nvsr->pt_timing_frame_time_us); seq_puts(s, "SR Timing:\n"); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_PIXEL_CLOCK0, 2, &val); seq_printf(s, "\tPixel clock: \t%dHz\n", ret ? -1 : val * 20000); /* Horizontal SR timing */ ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING0, 2, &val); seq_printf(s, "\tHactive: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING2, 2, &val); seq_printf(s, "\tHblank: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING4, 2, &val); seq_printf(s, "\tHfp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING6, 2, &val); seq_printf(s, "\tHbp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING8, 1, &val); seq_printf(s, "\tHsync width: \t%d\n", val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING9, 1, &val); val2 = val & NVSR_SRMODE_TIMING9_HORZ_BORDER_MASK; val = (val & NVSR_SRMODE_TIMING9_HSYNC_POL_MASK) >> NVSR_SRMODE_TIMING9_HSYNC_POL_SHIFT; seq_printf(s, "\tHborder: \t%d\n", ret ? -1 : val2); seq_printf(s, "\tHsync pulse polarity: \t%d\n", ret ? -1 : val); /* Vertical SR timing */ ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING10, 2, &val); seq_printf(s, "\tVactive: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING12, 2, &val); seq_printf(s, "\tVblank \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING14, 2, &val); seq_printf(s, "\tVfp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING16, 2, &val); seq_printf(s, "\tVbp: \t\t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING18, 1, &val); seq_printf(s, "\tVsync width: \t%d\n", ret ? -1 : val); ret = nvsr->reg_ops.read(nvsr, NVSR_SRMODE_TIMING19, 1, &val); val2 = val & NVSR_SRMODE_TIMING19_VERT_BORDER; val = (val & NVSR_SRMODE_TIMING19_VSYNC_POL_MASK) >> NVSR_SRMODE_TIMING19_VSYNC_POL_SHIFT; seq_printf(s, "\tVborder: \t%d\n", ret ? -1 : val2); seq_printf(s, "\tVsync pulse polarity: \t%d\n", ret ? -1 : val); seq_printf(s, "\tFrame time: \t%dus\n", nvsr->sr_timing_frame_time_us); tegra_dc_nvsr_put(nvsr); return 0; } static int nvsr_dbg_status_open(struct inode *inode, struct file *file) { return single_open(file, nvsr_dbg_status_show, inode->i_private); } static ssize_t nvsr_dbg_read_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct seq_file *m = file->private_data; struct tegra_dc_nvsr_data *nvsr = m ? m->private : NULL; char buf[128], *addr, *size, *pbuf; u32 uaddr, usize, val; if (!nvsr) return -ENODEV; if (sizeof(buf) <= count) return -EFAULT; if (copy_from_user(buf, userbuf, sizeof(buf))) return -EFAULT; buf[count] = '\0'; strim(buf); pbuf = buf; addr = strsep(&pbuf, " "); size = strsep(&pbuf, " "); if (!size) { NVSR_INFO("Syntax error: \n"); return -EINVAL; } if (kstrtou32(addr, 16, &uaddr)) { NVSR_INFO("Can't parse address \"%s\"\n", addr); return -EINVAL; } if (kstrtou32(size, 10, &usize)) { NVSR_INFO("Can't parse data size\"%s\"\n", size); return -EINVAL; } NVSR_INFO("Reading %u bytes from address 0x%x:\n", usize, uaddr); tegra_dc_nvsr_get(nvsr); if (nvsr->reg_ops.read(nvsr, uaddr, usize, &val)) NVSR_INFO("Failed to read register.\n"); NVSR_INFO("NVSR: Read back 0x%x\n", val); tegra_dc_nvsr_put(nvsr); return count; } static ssize_t nvsr_dbg_write_write(struct file *file, const char __user *userbuf, size_t count, loff_t *ppos) { struct seq_file *m = file->private_data; struct tegra_dc_nvsr_data *nvsr = m ? m->private : NULL; char buf[128], *addr, *val, *size, *pbuf; u32 uaddr, usize; u64 uval; if (!nvsr) return -ENODEV; if (sizeof(buf) <= count) return -EFAULT; if (copy_from_user(buf, userbuf, sizeof(buf))) return -EFAULT; buf[count] = '\0'; strim(buf); pbuf = buf; addr = strsep(&pbuf, " "); val = strsep(&pbuf, " "); size = strsep(&pbuf, " "); if (!val || !size) { NVSR_INFO("Syntax error: \n"); return -EINVAL; } if (kstrtou32(addr, 16, &uaddr)) { NVSR_INFO("Can't parse address \"%s\"\n", addr); return -EINVAL; } if (kstrtou64(val, 16, &uval)) { NVSR_INFO("Can't parse value\"%s\"\n", val); return -EINVAL; } if (kstrtou32(size, 10, &usize)) { NVSR_INFO("Can't parse data size\"%s\"\n", size); return -EINVAL; } NVSR_INFO("Writing value of 0x%llx (%u bytes) to address 0x%x...\n", uval, usize, uaddr); tegra_dc_nvsr_get(nvsr); if (nvsr->reg_ops.write(nvsr, uaddr, usize, uval)) NVSR_INFO("Failed to write to register.\n"); tegra_dc_nvsr_put(nvsr); return count; } static const struct file_operations nvsr_dbg_read_fops = { .open = nvsr_dbg_read_open, .read = seq_read, .write = nvsr_dbg_read_write, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations nvsr_dbg_write_fops = { .open = nvsr_dbg_write_open, .read = seq_read, .write = nvsr_dbg_write_write, .llseek = seq_lseek, .release = single_release, }; static const struct file_operations nvsr_dbg_status_fops = { .open = nvsr_dbg_status_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static void tegra_dc_nvsr_debug_create(struct tegra_dc_nvsr_data *nvsr) { struct dentry *retval, *nvsrdir; nvsrdir = debugfs_create_dir("tegra_nvsr", NULL); if (!nvsrdir) return; retval = debugfs_create_file("read", S_IRUGO | S_IWUGO, nvsrdir, nvsr, &nvsr_dbg_read_fops); if (!retval) goto free_out; retval = debugfs_create_file("write", S_IRUGO | S_IWUGO, nvsrdir, nvsr, &nvsr_dbg_write_fops); if (!retval) goto free_out; retval = debugfs_create_file("status", S_IRUGO, nvsrdir, nvsr, &nvsr_dbg_status_fops); if (!retval) goto free_out; return; free_out: debugfs_remove_recursive(nvsrdir); return; } #else static inline void tegra_dc_nvsr_debug_create(struct tegra_dc_nvsr_data *nvsr) { } #endif static int tegra_dc_nvsr_init(struct tegra_dc *dc) { int ret = 0; struct tegra_dc_nvsr_data *nvsr = kzalloc(sizeof(*nvsr), GFP_KERNEL); if (!nvsr) { ret = -ENOMEM; goto err_nvsr_init_out; } nvsr->dc = dc; dc->nvsr = nvsr; switch (dc->out->type) { case TEGRA_DC_OUT_NVSR_DP: tegra_dc_nvsr_init_out_ops(nvsr, &tegra_dc_dp_ops); if (nvsr->out_ops.init) { ret = nvsr->out_ops.init(dc); NVSR_RETV(ret, "Out ops init failed.\n"); } nvsr->out_data.dp = tegra_dc_get_outdata(dc); nvsr->out_clk = nvsr->out_data.dp->dpaux_clk; nvsr->reg_ops.read = tegra_nvsr_read_dpaux; nvsr->reg_ops.write = tegra_nvsr_write_dpaux; break; default: pr_err("NVSR output interface not found.\n"); ret = -EINVAL; goto err_nvsr_init_free; } tegra_dc_nvsr_debug_create(nvsr); init_completion(&nvsr->framelock_comp); nvsr->enable = true; return ret; err_nvsr_init_free: kfree(nvsr); err_nvsr_init_out: return ret; } struct tegra_dc_out_ops tegra_dc_nvsr_ops = { .init = tegra_dc_nvsr_init, }; static struct kobject *nvsr_kobj; static ssize_t enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct device *dev = container_of((kobj->parent), struct device, kobj); struct platform_device *ndev = to_platform_device(dev); struct tegra_dc *dc = platform_get_drvdata(ndev); return snprintf(buf, PAGE_SIZE, "%d\n", dc->nvsr->enable); } static ssize_t enable_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct device *dev = container_of((kobj->parent), struct device, kobj); struct platform_device *ndev = to_platform_device(dev); struct tegra_dc *dc = platform_get_drvdata(ndev); struct tegra_dc_nvsr_data *nvsr = dc->nvsr; u32 val; if (kstrtou32(buf, 10, &val) < 0) { dev_err(dev, "NVSR: bad enable input: \"%s\"\n", buf); return -EINVAL; } tegra_dc_nvsr_get(nvsr); if (val) NVSR_RETV(tegra_dc_nvsr_enable_nvsr(nvsr), "Failed to enable NVSR\n") else NVSR_RETV(tegra_dc_nvsr_disable_nvsr(nvsr), "Failed to disable NVSR\n") tegra_dc_nvsr_put(nvsr); return count; } static struct kobj_attribute nvsr_attr_enable = __ATTR(enable, S_IRUGO|S_IWUSR, enable_show, enable_store); static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct device *dev = container_of((kobj->parent), struct device, kobj); struct platform_device *ndev = to_platform_device(dev); struct tegra_dc *dc = platform_get_drvdata(ndev); return snprintf(buf, PAGE_SIZE, "%d\n", dc->nvsr->idle_active); } static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { struct device *dev = container_of((kobj->parent), struct device, kobj); struct platform_device *ndev = to_platform_device(dev); struct tegra_dc *dc = platform_get_drvdata(ndev); struct tegra_dc_nvsr_data *nvsr = dc->nvsr; u32 val; if (kstrtou32(buf, 10, &val) < 0) { dev_err(dev, "NVSR: bad enable input: \"%s\"\n", buf); return -EINVAL; } if (val) NVSR_RETV(tegra_dc_nvsr_enter_idle(nvsr), "Failed to enter idle\n") else NVSR_RETV(tegra_dc_nvsr_exit_idle(nvsr), "Failed to exit SR\n") return count; } static struct kobj_attribute nvsr_attr_idle = __ATTR(idle, S_IRUGO|S_IWUSR, idle_show, idle_store); static struct attribute *nvsr_attrs[] = { &nvsr_attr_enable.attr, &nvsr_attr_idle.attr, NULL, }; static struct attribute_group nvsr_attr_group = { .attrs = nvsr_attrs, }; /* Sysfs initializer */ int nvsr_create_sysfs(struct device *dev) { int retval; nvsr_kobj = kobject_create_and_add("nvsr", &dev->kobj); if (!nvsr_kobj) return -ENOMEM; retval = sysfs_create_group(nvsr_kobj, &nvsr_attr_group); if (retval) { kobject_put(nvsr_kobj); dev_err(dev, "%s: failed to create attributes\n", __func__); } return retval; } /* Sysfs destructor */ void nvsr_remove_sysfs(struct device *dev) { if (nvsr_kobj) { sysfs_remove_group(nvsr_kobj, &nvsr_attr_group); kobject_put(nvsr_kobj); } }