/* * Copyright (c) 2013 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Amit Daniel Kachhap * * EXYNOS5440 - CPU frequency scaling support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include /* Register definitions */ #define XMU_DVFS_CTRL 0x0060 #define XMU_PMU_P0_7 0x0064 #define XMU_C0_3_PSTATE 0x0090 #define XMU_P_LIMIT 0x00a0 #define XMU_P_STATUS 0x00a4 #define XMU_PMUEVTEN 0x00d0 #define XMU_PMUIRQEN 0x00d4 #define XMU_PMUIRQ 0x00d8 /* PMU mask and shift definations */ #define P_VALUE_MASK 0x7 #define XMU_DVFS_CTRL_EN_SHIFT 0 #define P0_7_CPUCLKDEV_SHIFT 21 #define P0_7_CPUCLKDEV_MASK 0x7 #define P0_7_ATBCLKDEV_SHIFT 18 #define P0_7_ATBCLKDEV_MASK 0x7 #define P0_7_CSCLKDEV_SHIFT 15 #define P0_7_CSCLKDEV_MASK 0x7 #define P0_7_CPUEMA_SHIFT 28 #define P0_7_CPUEMA_MASK 0xf #define P0_7_L2EMA_SHIFT 24 #define P0_7_L2EMA_MASK 0xf #define P0_7_VDD_SHIFT 8 #define P0_7_VDD_MASK 0x7f #define P0_7_FREQ_SHIFT 0 #define P0_7_FREQ_MASK 0xff #define C0_3_PSTATE_VALID_SHIFT 8 #define C0_3_PSTATE_CURR_SHIFT 4 #define C0_3_PSTATE_NEW_SHIFT 0 #define PSTATE_CHANGED_EVTEN_SHIFT 0 #define PSTATE_CHANGED_IRQEN_SHIFT 0 #define PSTATE_CHANGED_SHIFT 0 /* some constant values for clock divider calculation */ #define CPU_DIV_FREQ_MAX 500 #define CPU_DBG_FREQ_MAX 375 #define CPU_ATB_FREQ_MAX 500 #define PMIC_LOW_VOLT 0x30 #define PMIC_HIGH_VOLT 0x28 #define CPUEMA_HIGH 0x2 #define CPUEMA_MID 0x4 #define CPUEMA_LOW 0x7 #define L2EMA_HIGH 0x1 #define L2EMA_MID 0x3 #define L2EMA_LOW 0x4 #define DIV_TAB_MAX 2 /* frequency unit is 20MHZ */ #define FREQ_UNIT 20 #define MAX_VOLTAGE 1550000 /* In microvolt */ #define VOLTAGE_STEP 12500 /* In microvolt */ #define CPUFREQ_NAME "exynos5440_dvfs" #define DEF_TRANS_LATENCY 100000 enum cpufreq_level_index { L0, L1, L2, L3, L4, L5, L6, L7, L8, L9, }; #define CPUFREQ_LEVEL_END (L7 + 1) struct exynos_dvfs_data { void __iomem *base; struct resource *mem; int irq; struct clk *cpu_clk; unsigned int latency; struct cpufreq_frequency_table *freq_table; unsigned int freq_count; struct device *dev; bool dvfs_enabled; struct work_struct irq_work; }; static struct exynos_dvfs_data *dvfs_info; static DEFINE_MUTEX(cpufreq_lock); static struct cpufreq_freqs freqs; static int init_div_table(void) { struct cpufreq_frequency_table *pos, *freq_tbl = dvfs_info->freq_table; unsigned int tmp, clk_div, ema_div, freq, volt_id; struct dev_pm_opp *opp; rcu_read_lock(); cpufreq_for_each_entry(pos, freq_tbl) { opp = dev_pm_opp_find_freq_exact(dvfs_info->dev, pos->frequency * 1000, true); if (IS_ERR(opp)) { rcu_read_unlock(); dev_err(dvfs_info->dev, "failed to find valid OPP for %u KHZ\n", pos->frequency); return PTR_ERR(opp); } freq = pos->frequency / 1000; /* In MHZ */ clk_div = ((freq / CPU_DIV_FREQ_MAX) & P0_7_CPUCLKDEV_MASK) << P0_7_CPUCLKDEV_SHIFT; clk_div |= ((freq / CPU_ATB_FREQ_MAX) & P0_7_ATBCLKDEV_MASK) << P0_7_ATBCLKDEV_SHIFT; clk_div |= ((freq / CPU_DBG_FREQ_MAX) & P0_7_CSCLKDEV_MASK) << P0_7_CSCLKDEV_SHIFT; /* Calculate EMA */ volt_id = dev_pm_opp_get_voltage(opp); volt_id = (MAX_VOLTAGE - volt_id) / VOLTAGE_STEP; if (volt_id < PMIC_HIGH_VOLT) { ema_div = (CPUEMA_HIGH << P0_7_CPUEMA_SHIFT) | (L2EMA_HIGH << P0_7_L2EMA_SHIFT); } else if (volt_id > PMIC_LOW_VOLT) { ema_div = (CPUEMA_LOW << P0_7_CPUEMA_SHIFT) | (L2EMA_LOW << P0_7_L2EMA_SHIFT); } else { ema_div = (CPUEMA_MID << P0_7_CPUEMA_SHIFT) | (L2EMA_MID << P0_7_L2EMA_SHIFT); } tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT) | ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT)); __raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 * (pos - freq_tbl)); } rcu_read_unlock(); return 0; } static void exynos_enable_dvfs(unsigned int cur_frequency) { unsigned int tmp, cpu; struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table; struct cpufreq_frequency_table *pos; /* Disable DVFS */ __raw_writel(0, dvfs_info->base + XMU_DVFS_CTRL); /* Enable PSTATE Change Event */ tmp = __raw_readl(dvfs_info->base + XMU_PMUEVTEN); tmp |= (1 << PSTATE_CHANGED_EVTEN_SHIFT); __raw_writel(tmp, dvfs_info->base + XMU_PMUEVTEN); /* Enable PSTATE Change IRQ */ tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQEN); tmp |= (1 << PSTATE_CHANGED_IRQEN_SHIFT); __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN); /* Set initial performance index */ cpufreq_for_each_entry(pos, freq_table) if (pos->frequency == cur_frequency) break; if (pos->frequency == CPUFREQ_TABLE_END) { dev_crit(dvfs_info->dev, "Boot up frequency not supported\n"); /* Assign the highest frequency */ pos = freq_table; cur_frequency = pos->frequency; } dev_info(dvfs_info->dev, "Setting dvfs initial frequency = %uKHZ", cur_frequency); for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) { tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT); tmp |= ((pos - freq_table) << C0_3_PSTATE_NEW_SHIFT); __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); } /* Enable DVFS */ __raw_writel(1 << XMU_DVFS_CTRL_EN_SHIFT, dvfs_info->base + XMU_DVFS_CTRL); } static int exynos_target(struct cpufreq_policy *policy, unsigned int index) { unsigned int tmp; int i; struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table; mutex_lock(&cpufreq_lock); freqs.old = policy->cur; freqs.new = freq_table[index].frequency; cpufreq_freq_transition_begin(policy, &freqs); /* Set the target frequency in all C0_3_PSTATE register */ for_each_cpu(i, policy->cpus) { tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + i * 4); tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT); tmp |= (index << C0_3_PSTATE_NEW_SHIFT); __raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4); } mutex_unlock(&cpufreq_lock); return 0; } static void exynos_cpufreq_work(struct work_struct *work) { unsigned int cur_pstate, index; struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table; /* Ensure we can access cpufreq structures */ if (unlikely(dvfs_info->dvfs_enabled == false)) goto skip_work; mutex_lock(&cpufreq_lock); freqs.old = policy->cur; cur_pstate = __raw_readl(dvfs_info->base + XMU_P_STATUS); if (cur_pstate >> C0_3_PSTATE_VALID_SHIFT & 0x1) index = (cur_pstate >> C0_3_PSTATE_CURR_SHIFT) & P_VALUE_MASK; else index = (cur_pstate >> C0_3_PSTATE_NEW_SHIFT) & P_VALUE_MASK; if (likely(index < dvfs_info->freq_count)) { freqs.new = freq_table[index].frequency; } else { dev_crit(dvfs_info->dev, "New frequency out of range\n"); freqs.new = freqs.old; } cpufreq_freq_transition_end(policy, &freqs, 0); cpufreq_cpu_put(policy); mutex_unlock(&cpufreq_lock); skip_work: Instead of using DVBv3 parameters, rely on DVBv5 parameters to set the tuner. Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/common/tuners/xc4000.c')
-rw-r--r--drivers/media/common/tuners/xc4000.c97
1 files changed, 42 insertions, 55 deletions
diff --git a/drivers/media/common/tuners/xc4000.c b/drivers/media/common/tuners/xc4000.c
index 21a7b094a774..ee6db66143f1 100644
--- a/drivers/media/common/tuners/xc4000.c
+++ b/drivers/media/common/tuners/xc4000.c
@@ -1124,80 +1124,67 @@ static void xc_debug_dump(struct xc4000_priv *priv)
static int xc4000_set_params(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params)
{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ u32 delsys = c->delivery_system;
+ u32 bw = c->bandwidth_hz;
struct xc4000_priv *priv = fe->tuner_priv;
unsigned int type;
int ret = -EREMOTEIO;
- dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency);
+ dprintk(1, "%s() frequency=%d (Hz)\n", __func__, c->frequency);
mutex_lock(&priv->lock);
- if (fe->ops.info.type == FE_ATSC) {
- dprintk(1, "%s() ATSC\n", __func__);
- switch (params->u.vsb.modulation) {
- case VSB_8:
- case VSB_16:
- dprintk(1, "%s() VSB modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_AIR;
- priv->freq_hz = params->frequency - 1750000;
- priv->bandwidth = BANDWIDTH_6_MHZ;
- priv->video_standard = XC4000_DTV6;
- type = DTV6;
- break;
- case QAM_64:
- case QAM_256:
- case QAM_AUTO:
- dprintk(1, "%s() QAM modulation\n", __func__);
- priv->rf_mode = XC_RF_MODE_CABLE;
- priv->freq_hz = params->frequency - 1750000;
- priv->bandwidth = BANDWIDTH_6_MHZ;
- priv->video_standard = XC4000_DTV6;
- type = DTV6;
- break;
- default:
- ret = -EINVAL;
- goto fail;
- }
- } else if (fe->ops.info.type == FE_OFDM) {
+ switch (delsys) {
+ case SYS_ATSC:
+ dprintk(1, "%s() VSB modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_AIR;
+ priv->freq_hz = c->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = XC4000_DTV6;
+ type = DTV6;
+ break;
+ case SYS_DVBC_ANNEX_B:
+ dprintk(1, "%s() QAM modulation\n", __func__);
+ priv->rf_mode = XC_RF_MODE_CABLE;
+ priv->freq_hz = c->frequency - 1750000;
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+ priv->video_standard = XC4000_DTV6;
+ type = DTV6;
+ break;
+ case SYS_DVBT:
+ case SYS_DVBT2:
dprintk(1, "%s() OFDM\n", __func__);
- switch (params->u.ofdm.bandwidth) {
- case BANDWIDTH_6_MHZ: