static ssize_t level_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct lcdfreq_info *info = dev_get_lcdfreq(dev); unsigned int value; int ret; if (!info->enable) { dev_err(info->dev, "%s reject. enable flag is %d\n", __func__, info->enable); return -EINVAL; } ret = kstrtoul(buf, 0, (unsigned long *)&value); dev_info(info->dev, "%s :: value=%d\n", __func__, value); if (value >= LEVEL_MAX) return -EINVAL; if (value) ret = lcdfreq_lock(info->dev, value); else ret = lcdfreq_lock_free(info->dev); if (ret) { dev_err(info->dev, "%s skip\n", __func__); return -EINVAL; } #ifdef CONFIG_MACH_T0 tsp_lcd_infom((bool *)value); #endif return count; }
static ssize_t usage_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lcdfreq_info *info = dev_get_lcdfreq(dev); return sprintf(buf, "%d\n", atomic_read(&info->usage)); }
static int set_lcdfreq_div(struct device *dev, enum lcdfreq_level level) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); u32 ret; mutex_lock(&lcdfreq->lock); if (!lcdfreq->enable) { dev_err(dev, "%s reject. enable flag is %d\n", __func__, lcdfreq->enable); ret = -EINVAL; goto exit; } ret = set_div(dev, lcdfreq->table[level].cmu_div); if (ret) { dev_err(dev, "fail to change lcd freq\n"); goto exit; } lcdfreq->level = level; exit: mutex_unlock(&lcdfreq->lock); return ret; }
static int get_divider(struct device *dev) { struct fb_info *fb = dev_get_drvdata(dev); struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); struct clksrc_clk *clksrc; struct clk *clk; u32 rate, reg; u8 fimd_div, i; lcdfreq->clksrc = clksrc = get_clksrc(dev->parent); clk = clk_get_parent(&clksrc->clk); rate = clk_get_rate(clk); lcdfreq->table[NORMAL].cmu_div = DIV_ROUND_CLOSEST(rate, lcdfreq->table[NORMAL].vclk); lcdfreq->table[LIMIT].cmu_div = DIV_ROUND_CLOSEST(rate, lcdfreq->table[LIMIT].vclk); if (lcdfreq->table[LIMIT].cmu_div > (1 << clksrc->reg_div.size)) fimd_div = gcd(lcdfreq->table[NORMAL].cmu_div, lcdfreq->table[LIMIT].cmu_div); else fimd_div = 1; dev_info(dev, "%s rate is %d, fimd div=%d\n", clk->name, rate, fimd_div); reg = get_fimd_divider(dev) + 1; if ((!fimd_div) || (fimd_div > 256) || (fimd_div != reg)) { dev_info(dev, "%s skip, fimd div=%d, reg=%d\n", __func__, fimd_div, reg); goto err; } for (i = 0; i < LEVEL_MAX; i++) { lcdfreq->table[i].cmu_div /= fimd_div; if (lcdfreq->table[i].cmu_div > (1 << clksrc->reg_div.size)) { dev_info(fb->dev, "%s skip, cmu div=%d\n", __func__, lcdfreq->table[i].cmu_div); goto err; } dev_info(dev, "%dhz div is %d\n", lcdfreq->table[i].hz, lcdfreq->table[i].cmu_div); lcdfreq->table[i].cmu_div--; } reg = get_div(dev); if (lcdfreq->table[NORMAL].cmu_div != reg) { dev_info(dev, "%s skip, cmu div=%d, reg=%d\n", __func__, lcdfreq->table[NORMAL].cmu_div, reg); goto err; } for (i = 0; i < LEVEL_MAX; i++) { reg = lcdfreq->table[i].cmu_div; lcdfreq->table[i].cmu_div = (reg << clksrc->reg_div.size) | reg; } return 0; err: return -EINVAL; }
static unsigned int get_vstatus(struct device *dev) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); u32 reg; reg = readl(lcdfreq->ielcd_regs + IELCD_VIDCON1); reg &= VIDCON1_VSTATUS_MASK; return reg; }
static ssize_t time_in_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lcdfreq_info *info = dev_get_lcdfreq(dev); do_time_slice(info->level); return sprintf(buf, "%llu %d (60Hz)\n%llu %d (40Hz)\n", (unsigned long long)time_in_state[NORMAL], NORMAL, (unsigned long long)time_in_state[LIMIT], LIMIT); }
static unsigned char get_fimd_divider(struct device *dev) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); unsigned int reg; reg = readl(lcdfreq->fimd_regs + VIDCON0); reg &= VIDCON0_CLKVAL_F_MASK; reg >>= VIDCON0_CLKVAL_F_SHIFT; return reg; }
static ssize_t level_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lcdfreq_info *info = dev_get_lcdfreq(dev); if (!info->enable) { dev_err(info->dev, "%s reject. enable flag is %d\n", __func__, info->enable); return -EINVAL; } return sprintf(buf, "%d, div=%d\n", info->table[info->level].hz, get_div(info->dev)); }
static int get_div(struct device *dev) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); struct clksrc_clk *clksrc = lcdfreq->clksrc; u32 reg = __raw_readl(clksrc->reg_div.reg); u32 mask = reg_mask(clksrc->reg_div.shift, clksrc->reg_div.size); reg &= mask; reg >>= clksrc->reg_div.shift; return reg; }
static void reset_div(struct device *dev) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); struct clksrc_clk *clksrc = lcdfreq->clksrc; u32 reg; reg = __raw_readl(clksrc->reg_div.reg); reg &= ~(0xff); reg |= lcdfreq->table[lcdfreq->level].cmu_div; writel(reg, clksrc->reg_div.reg); }
static int lcdfreq_lock_free(struct device *dev) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); int ret; if (atomic_read(&lcdfreq->usage)) ret = set_lcdfreq_div(dev, NORMAL); else { dev_err(dev, "lcd freq is already normal state\n"); return -EINVAL; } if (!ret) { mutex_lock(&lcdfreq->lock); atomic_dec(&lcdfreq->usage); mutex_unlock(&lcdfreq->lock); cancel_delayed_work(&lcdfreq->work); } return ret; }
static int lcdfreq_lock(struct device *dev) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); int ret; if (!atomic_read(&lcdfreq->usage)) ret = set_lcdfreq_div(dev, LIMIT); else { dev_err(dev, "lcd freq is already limit state\n"); return -EINVAL; } if (!ret) { mutex_lock(&lcdfreq->lock); atomic_inc(&lcdfreq->usage); mutex_unlock(&lcdfreq->lock); schedule_delayed_work(&lcdfreq->work, 0); } return ret; }
static int set_div(struct device *dev, u32 div) { struct lcdfreq_info *lcdfreq = dev_get_lcdfreq(dev); struct clksrc_clk *clksrc = lcdfreq->clksrc; u32 mask = reg_mask(clksrc->reg_div.shift, clksrc->reg_div.size); unsigned long flags; u32 reg, count = 1000000; do { spin_lock_irqsave(&lcdfreq->slock, flags); reg = __raw_readl(clksrc->reg_div.reg); if ((reg & mask) == (div & mask)) { spin_unlock_irqrestore(&lcdfreq->slock, flags); return -EINVAL; } reg &= ~(0xff); reg |= div; if (VSTATUS_IS_ACTIVE(get_vstatus(dev))) { if (VSTATUS_IS_FRONT(get_vstatus(dev))) { writel(reg, clksrc->reg_div.reg); spin_unlock_irqrestore(&lcdfreq->slock, flags); dev_info(dev, "%x, %d\n", __raw_readl(clksrc->reg_div.reg), 1000000-count); return 0; } } spin_unlock_irqrestore(&lcdfreq->slock, flags); count--; } while (count); dev_err(dev, "%s fail, div=%d\n", __func__, div); return -EINVAL; }