linux/sound/soc/codecs/rl6231.c
<<
>>
Prefs
   1/*
   2 * rl6231.c - RL6231 class device shared support
   3 *
   4 * Copyright 2014 Realtek Semiconductor Corp.
   5 *
   6 * Author: Oder Chiou <oder_chiou@realtek.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/regmap.h>
  15
  16#include "rl6231.h"
  17
  18/**
  19 * rl6231_get_pre_div - Return the value of pre divider.
  20 *
  21 * @map: map for setting.
  22 * @reg: register.
  23 * @sft: shift.
  24 *
  25 * Return the value of pre divider from given register value.
  26 * Return negative error code for unexpected register value.
  27 */
  28int rl6231_get_pre_div(struct regmap *map, unsigned int reg, int sft)
  29{
  30        int pd, val;
  31
  32        regmap_read(map, reg, &val);
  33
  34        val = (val >> sft) & 0x7;
  35
  36        switch (val) {
  37        case 0:
  38        case 1:
  39        case 2:
  40        case 3:
  41                pd = val + 1;
  42                break;
  43        case 4:
  44                pd = 6;
  45                break;
  46        case 5:
  47                pd = 8;
  48                break;
  49        case 6:
  50                pd = 12;
  51                break;
  52        case 7:
  53                pd = 16;
  54                break;
  55        default:
  56                pd = -EINVAL;
  57                break;
  58        }
  59
  60        return pd;
  61}
  62EXPORT_SYMBOL_GPL(rl6231_get_pre_div);
  63
  64/**
  65 * rl6231_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
  66 *
  67 * @rate: base clock rate.
  68 *
  69 * Choose divider parameter that gives the highest possible DMIC frequency in
  70 * 1MHz - 3MHz range.
  71 */
  72int rl6231_calc_dmic_clk(int rate)
  73{
  74        int div[] = {2, 3, 4, 6, 8, 12};
  75        int i;
  76
  77        if (rate < 1000000 * div[0]) {
  78                pr_warn("Base clock rate %d is too low\n", rate);
  79                return -EINVAL;
  80        }
  81
  82        for (i = 0; i < ARRAY_SIZE(div); i++) {
  83                if ((div[i] % 3) == 0)
  84                        continue;
  85                /* find divider that gives DMIC frequency below 3.072MHz */
  86                if (3072000 * div[i] >= rate)
  87                        return i;
  88        }
  89
  90        pr_warn("Base clock rate %d is too high\n", rate);
  91        return -EINVAL;
  92}
  93EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
  94
  95struct pll_calc_map {
  96        unsigned int pll_in;
  97        unsigned int pll_out;
  98        int k;
  99        int n;
 100        int m;
 101        bool m_bp;
 102};
 103
 104static const struct pll_calc_map pll_preset_table[] = {
 105        {19200000,  24576000,  3, 30, 3, false},
 106};
 107
 108/**
 109 * rl6231_pll_calc - Calcualte PLL M/N/K code.
 110 * @freq_in: external clock provided to codec.
 111 * @freq_out: target clock which codec works on.
 112 * @pll_code: Pointer to structure with M, N, K and bypass flag.
 113 *
 114 * Calcualte M/N/K code to configure PLL for codec.
 115 *
 116 * Returns 0 for success or negative error code.
 117 */
 118int rl6231_pll_calc(const unsigned int freq_in,
 119        const unsigned int freq_out, struct rl6231_pll_code *pll_code)
 120{
 121        int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
 122        int i, k, red, n_t, pll_out, in_t, out_t;
 123        int n = 0, m = 0, m_t = 0;
 124        int red_t = abs(freq_out - freq_in);
 125        bool bypass = false;
 126
 127        if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
 128                return -EINVAL;
 129
 130        for (i = 0; i < ARRAY_SIZE(pll_preset_table); i++) {
 131                if (freq_in == pll_preset_table[i].pll_in &&
 132                        freq_out == pll_preset_table[i].pll_out) {
 133                        k = pll_preset_table[i].k;
 134                        m = pll_preset_table[i].m;
 135                        n = pll_preset_table[i].n;
 136                        bypass = pll_preset_table[i].m_bp;
 137                        pr_debug("Use preset PLL parameter table\n");
 138                        goto code_find;
 139                }
 140        }
 141
 142        k = 100000000 / freq_out - 2;
 143        if (k > RL6231_PLL_K_MAX)
 144                k = RL6231_PLL_K_MAX;
 145        for (n_t = 0; n_t <= max_n; n_t++) {
 146                in_t = freq_in / (k + 2);
 147                pll_out = freq_out / (n_t + 2);
 148                if (in_t < 0)
 149                        continue;
 150                if (in_t == pll_out) {
 151                        bypass = true;
 152                        n = n_t;
 153                        goto code_find;
 154                }
 155                red = abs(in_t - pll_out);
 156                if (red < red_t) {
 157                        bypass = true;
 158                        n = n_t;
 159                        m = m_t;
 160                        if (red == 0)
 161                                goto code_find;
 162                        red_t = red;
 163                }
 164                for (m_t = 0; m_t <= max_m; m_t++) {
 165                        out_t = in_t / (m_t + 2);
 166                        red = abs(out_t - pll_out);
 167                        if (red < red_t) {
 168                                bypass = false;
 169                                n = n_t;
 170                                m = m_t;
 171                                if (red == 0)
 172                                        goto code_find;
 173                                red_t = red;
 174                        }
 175                }
 176        }
 177        pr_debug("Only get approximation about PLL\n");
 178
 179code_find:
 180
 181        pll_code->m_bp = bypass;
 182        pll_code->m_code = m;
 183        pll_code->n_code = n;
 184        pll_code->k_code = k;
 185        return 0;
 186}
 187EXPORT_SYMBOL_GPL(rl6231_pll_calc);
 188
 189int rl6231_get_clk_info(int sclk, int rate)
 190{
 191        int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
 192
 193        if (sclk <= 0 || rate <= 0)
 194                return -EINVAL;
 195
 196        rate = rate << 8;
 197        for (i = 0; i < ARRAY_SIZE(pd); i++)
 198                if (sclk == rate * pd[i])
 199                        return i;
 200
 201        return -EINVAL;
 202}
 203EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
 204
 205MODULE_DESCRIPTION("RL6231 class device shared support");
 206MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
 207MODULE_LICENSE("GPL v2");
 208