rl6231.c 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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. #include <linux/module.h>
  13. #include "rl6231.h"
  14. /**
  15. * rl6231_calc_dmic_clk - Calculate the parameter of dmic.
  16. *
  17. * @rate: base clock rate.
  18. *
  19. * Choose dmic clock between 1MHz and 3MHz.
  20. * It is better for clock to approximate 3MHz.
  21. */
  22. int rl6231_calc_dmic_clk(int rate)
  23. {
  24. int div[] = {2, 3, 4, 6, 8, 12}, idx = -EINVAL;
  25. int i, red, bound, temp;
  26. red = 3000000 * 12;
  27. for (i = 0; i < ARRAY_SIZE(div); i++) {
  28. bound = div[i] * 3000000;
  29. if (rate > bound)
  30. continue;
  31. temp = bound - rate;
  32. if (temp < red) {
  33. red = temp;
  34. idx = i;
  35. }
  36. }
  37. return idx;
  38. }
  39. EXPORT_SYMBOL_GPL(rl6231_calc_dmic_clk);
  40. /**
  41. * rl6231_pll_calc - Calcualte PLL M/N/K code.
  42. * @freq_in: external clock provided to codec.
  43. * @freq_out: target clock which codec works on.
  44. * @pll_code: Pointer to structure with M, N, K and bypass flag.
  45. *
  46. * Calcualte M/N/K code to configure PLL for codec.
  47. *
  48. * Returns 0 for success or negative error code.
  49. */
  50. int rl6231_pll_calc(const unsigned int freq_in,
  51. const unsigned int freq_out, struct rl6231_pll_code *pll_code)
  52. {
  53. int max_n = RL6231_PLL_N_MAX, max_m = RL6231_PLL_M_MAX;
  54. int k, red, n_t, pll_out, in_t, out_t;
  55. int n = 0, m = 0, m_t = 0;
  56. int red_t = abs(freq_out - freq_in);
  57. bool bypass = false;
  58. if (RL6231_PLL_INP_MAX < freq_in || RL6231_PLL_INP_MIN > freq_in)
  59. return -EINVAL;
  60. k = 100000000 / freq_out - 2;
  61. if (k > RL6231_PLL_K_MAX)
  62. k = RL6231_PLL_K_MAX;
  63. for (n_t = 0; n_t <= max_n; n_t++) {
  64. in_t = freq_in / (k + 2);
  65. pll_out = freq_out / (n_t + 2);
  66. if (in_t < 0)
  67. continue;
  68. if (in_t == pll_out) {
  69. bypass = true;
  70. n = n_t;
  71. goto code_find;
  72. }
  73. red = abs(in_t - pll_out);
  74. if (red < red_t) {
  75. bypass = true;
  76. n = n_t;
  77. m = m_t;
  78. if (red == 0)
  79. goto code_find;
  80. red_t = red;
  81. }
  82. for (m_t = 0; m_t <= max_m; m_t++) {
  83. out_t = in_t / (m_t + 2);
  84. red = abs(out_t - pll_out);
  85. if (red < red_t) {
  86. bypass = false;
  87. n = n_t;
  88. m = m_t;
  89. if (red == 0)
  90. goto code_find;
  91. red_t = red;
  92. }
  93. }
  94. }
  95. pr_debug("Only get approximation about PLL\n");
  96. code_find:
  97. pll_code->m_bp = bypass;
  98. pll_code->m_code = m;
  99. pll_code->n_code = n;
  100. pll_code->k_code = k;
  101. return 0;
  102. }
  103. EXPORT_SYMBOL_GPL(rl6231_pll_calc);
  104. int rl6231_get_clk_info(int sclk, int rate)
  105. {
  106. int i, pd[] = {1, 2, 3, 4, 6, 8, 12, 16};
  107. if (sclk <= 0 || rate <= 0)
  108. return -EINVAL;
  109. rate = rate << 8;
  110. for (i = 0; i < ARRAY_SIZE(pd); i++)
  111. if (sclk == rate * pd[i])
  112. return i;
  113. return -EINVAL;
  114. }
  115. EXPORT_SYMBOL_GPL(rl6231_get_clk_info);
  116. MODULE_DESCRIPTION("RL6231 class device shared support");
  117. MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
  118. MODULE_LICENSE("GPL v2");