19a0dfe9bSDmitry Baryshkov /* 29a0dfe9bSDmitry Baryshkov * Copyright (C) 2009 Francisco Jerez. 39a0dfe9bSDmitry Baryshkov * All Rights Reserved. 49a0dfe9bSDmitry Baryshkov * 59a0dfe9bSDmitry Baryshkov * Permission is hereby granted, free of charge, to any person obtaining 69a0dfe9bSDmitry Baryshkov * a copy of this software and associated documentation files (the 79a0dfe9bSDmitry Baryshkov * "Software"), to deal in the Software without restriction, including 89a0dfe9bSDmitry Baryshkov * without limitation the rights to use, copy, modify, merge, publish, 99a0dfe9bSDmitry Baryshkov * distribute, sublicense, and/or sell copies of the Software, and to 109a0dfe9bSDmitry Baryshkov * permit persons to whom the Software is furnished to do so, subject to 119a0dfe9bSDmitry Baryshkov * the following conditions: 129a0dfe9bSDmitry Baryshkov * 139a0dfe9bSDmitry Baryshkov * The above copyright notice and this permission notice (including the 149a0dfe9bSDmitry Baryshkov * next paragraph) shall be included in all copies or substantial 159a0dfe9bSDmitry Baryshkov * portions of the Software. 169a0dfe9bSDmitry Baryshkov * 179a0dfe9bSDmitry Baryshkov * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 189a0dfe9bSDmitry Baryshkov * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 199a0dfe9bSDmitry Baryshkov * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 209a0dfe9bSDmitry Baryshkov * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 219a0dfe9bSDmitry Baryshkov * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 229a0dfe9bSDmitry Baryshkov * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 239a0dfe9bSDmitry Baryshkov * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 249a0dfe9bSDmitry Baryshkov * 259a0dfe9bSDmitry Baryshkov */ 269a0dfe9bSDmitry Baryshkov 279a0dfe9bSDmitry Baryshkov #include "ch7006_priv.h" 289a0dfe9bSDmitry Baryshkov 299a0dfe9bSDmitry Baryshkov const char * const ch7006_tv_norm_names[] = { 309a0dfe9bSDmitry Baryshkov [TV_NORM_PAL] = "PAL", 319a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_M] = "PAL-M", 329a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_N] = "PAL-N", 339a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_NC] = "PAL-Nc", 349a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_60] = "PAL-60", 359a0dfe9bSDmitry Baryshkov [TV_NORM_NTSC_M] = "NTSC-M", 369a0dfe9bSDmitry Baryshkov [TV_NORM_NTSC_J] = "NTSC-J", 379a0dfe9bSDmitry Baryshkov }; 389a0dfe9bSDmitry Baryshkov 399a0dfe9bSDmitry Baryshkov #define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \ 409a0dfe9bSDmitry Baryshkov .vdisplay = 480, \ 419a0dfe9bSDmitry Baryshkov .vtotal = 525, \ 429a0dfe9bSDmitry Baryshkov .hvirtual = 660 439a0dfe9bSDmitry Baryshkov 449a0dfe9bSDmitry Baryshkov #define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \ 459a0dfe9bSDmitry Baryshkov .vdisplay = 576, \ 469a0dfe9bSDmitry Baryshkov .vtotal = 625, \ 479a0dfe9bSDmitry Baryshkov .hvirtual = 810 489a0dfe9bSDmitry Baryshkov 499a0dfe9bSDmitry Baryshkov const struct ch7006_tv_norm_info ch7006_tv_norms[] = { 509a0dfe9bSDmitry Baryshkov [TV_NORM_NTSC_M] = { 519a0dfe9bSDmitry Baryshkov NTSC_LIKE_TIMINGS, 529a0dfe9bSDmitry Baryshkov .black_level = 0.339 * fixed1, 539a0dfe9bSDmitry Baryshkov .subc_freq = 3579545 * fixed1, 549a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC), 559a0dfe9bSDmitry Baryshkov .voffset = 0, 569a0dfe9bSDmitry Baryshkov }, 579a0dfe9bSDmitry Baryshkov [TV_NORM_NTSC_J] = { 589a0dfe9bSDmitry Baryshkov NTSC_LIKE_TIMINGS, 599a0dfe9bSDmitry Baryshkov .black_level = 0.286 * fixed1, 609a0dfe9bSDmitry Baryshkov .subc_freq = 3579545 * fixed1, 619a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J), 629a0dfe9bSDmitry Baryshkov .voffset = 0, 639a0dfe9bSDmitry Baryshkov }, 649a0dfe9bSDmitry Baryshkov [TV_NORM_PAL] = { 659a0dfe9bSDmitry Baryshkov PAL_LIKE_TIMINGS, 669a0dfe9bSDmitry Baryshkov .black_level = 0.3 * fixed1, 679a0dfe9bSDmitry Baryshkov .subc_freq = 4433618.75 * fixed1, 689a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL), 699a0dfe9bSDmitry Baryshkov .voffset = 0, 709a0dfe9bSDmitry Baryshkov }, 719a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_M] = { 729a0dfe9bSDmitry Baryshkov NTSC_LIKE_TIMINGS, 739a0dfe9bSDmitry Baryshkov .black_level = 0.339 * fixed1, 749a0dfe9bSDmitry Baryshkov .subc_freq = 3575611.433 * fixed1, 759a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M), 769a0dfe9bSDmitry Baryshkov .voffset = 16, 779a0dfe9bSDmitry Baryshkov }, 789a0dfe9bSDmitry Baryshkov 799a0dfe9bSDmitry Baryshkov /* The following modes seem to work right but they're 809a0dfe9bSDmitry Baryshkov * undocumented */ 819a0dfe9bSDmitry Baryshkov 829a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_N] = { 839a0dfe9bSDmitry Baryshkov PAL_LIKE_TIMINGS, 849a0dfe9bSDmitry Baryshkov .black_level = 0.339 * fixed1, 859a0dfe9bSDmitry Baryshkov .subc_freq = 4433618.75 * fixed1, 869a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL), 879a0dfe9bSDmitry Baryshkov .voffset = 0, 889a0dfe9bSDmitry Baryshkov }, 899a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_NC] = { 909a0dfe9bSDmitry Baryshkov PAL_LIKE_TIMINGS, 919a0dfe9bSDmitry Baryshkov .black_level = 0.3 * fixed1, 929a0dfe9bSDmitry Baryshkov .subc_freq = 3582056.25 * fixed1, 939a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL), 949a0dfe9bSDmitry Baryshkov .voffset = 0, 959a0dfe9bSDmitry Baryshkov }, 969a0dfe9bSDmitry Baryshkov [TV_NORM_PAL_60] = { 979a0dfe9bSDmitry Baryshkov NTSC_LIKE_TIMINGS, 989a0dfe9bSDmitry Baryshkov .black_level = 0.3 * fixed1, 999a0dfe9bSDmitry Baryshkov .subc_freq = 4433618.75 * fixed1, 1009a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M), 1019a0dfe9bSDmitry Baryshkov .voffset = 16, 1029a0dfe9bSDmitry Baryshkov }, 1039a0dfe9bSDmitry Baryshkov }; 1049a0dfe9bSDmitry Baryshkov 1059a0dfe9bSDmitry Baryshkov #define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \ 1069a0dfe9bSDmitry Baryshkov subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \ 1079a0dfe9bSDmitry Baryshkov .mode = { \ 1089a0dfe9bSDmitry Baryshkov .name = #hd "x" #vd, \ 1099a0dfe9bSDmitry Baryshkov .status = 0, \ 1109a0dfe9bSDmitry Baryshkov .type = DRM_MODE_TYPE_DRIVER, \ 1119a0dfe9bSDmitry Baryshkov .clock = f, \ 1129a0dfe9bSDmitry Baryshkov .hdisplay = hd, \ 1139a0dfe9bSDmitry Baryshkov .hsync_start = e_hd + 16, \ 1149a0dfe9bSDmitry Baryshkov .hsync_end = e_hd + 80, \ 1159a0dfe9bSDmitry Baryshkov .htotal = ht, \ 1169a0dfe9bSDmitry Baryshkov .hskew = 0, \ 1179a0dfe9bSDmitry Baryshkov .vdisplay = vd, \ 1189a0dfe9bSDmitry Baryshkov .vsync_start = vd + 10, \ 1199a0dfe9bSDmitry Baryshkov .vsync_end = vd + 26, \ 1209a0dfe9bSDmitry Baryshkov .vtotal = vt, \ 1219a0dfe9bSDmitry Baryshkov .vscan = 0, \ 1229a0dfe9bSDmitry Baryshkov .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \ 1239a0dfe9bSDmitry Baryshkov DRM_MODE_FLAG_##vsynp##VSYNC, \ 1249a0dfe9bSDmitry Baryshkov }, \ 1259a0dfe9bSDmitry Baryshkov .enc_hdisp = e_hd, \ 1269a0dfe9bSDmitry Baryshkov .enc_vdisp = e_vd, \ 1279a0dfe9bSDmitry Baryshkov .subc_coeff = subc * fixed1, \ 1289a0dfe9bSDmitry Baryshkov .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \ 1299a0dfe9bSDmitry Baryshkov bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \ 1309a0dfe9bSDmitry Baryshkov .valid_scales = scale_mask, \ 1319a0dfe9bSDmitry Baryshkov .valid_norms = norm_mask \ 1329a0dfe9bSDmitry Baryshkov } 1339a0dfe9bSDmitry Baryshkov 1349a0dfe9bSDmitry Baryshkov #define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \ 1359a0dfe9bSDmitry Baryshkov subc, scale, scale_mask, norm_mask) \ 1369a0dfe9bSDmitry Baryshkov __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \ 1379a0dfe9bSDmitry Baryshkov scale_mask, norm_mask, hd, vd) 1389a0dfe9bSDmitry Baryshkov 1399a0dfe9bSDmitry Baryshkov #define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \ 1409a0dfe9bSDmitry Baryshkov 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60) 1419a0dfe9bSDmitry Baryshkov 1429a0dfe9bSDmitry Baryshkov #define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC) 1439a0dfe9bSDmitry Baryshkov 1449a0dfe9bSDmitry Baryshkov const struct ch7006_mode ch7006_modes[] = { 1459a0dfe9bSDmitry Baryshkov MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE), 1469a0dfe9bSDmitry Baryshkov MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE), 1479a0dfe9bSDmitry Baryshkov MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE), 1489a0dfe9bSDmitry Baryshkov MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE), 1499a0dfe9bSDmitry Baryshkov MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE), 1509a0dfe9bSDmitry Baryshkov MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE), 1519a0dfe9bSDmitry Baryshkov MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE), 1529a0dfe9bSDmitry Baryshkov MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE), 1539a0dfe9bSDmitry Baryshkov MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE), 1549a0dfe9bSDmitry Baryshkov MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE), 1559a0dfe9bSDmitry Baryshkov MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE), 1569a0dfe9bSDmitry Baryshkov MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE), 1579a0dfe9bSDmitry Baryshkov MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE), 1589a0dfe9bSDmitry Baryshkov MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE), 1599a0dfe9bSDmitry Baryshkov MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE), 1609a0dfe9bSDmitry Baryshkov MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE), 1619a0dfe9bSDmitry Baryshkov MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE), 1629a0dfe9bSDmitry Baryshkov MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE), 1639a0dfe9bSDmitry Baryshkov MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE), 1649a0dfe9bSDmitry Baryshkov __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600), 1659a0dfe9bSDmitry Baryshkov MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE), 1669a0dfe9bSDmitry Baryshkov MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE), 1679a0dfe9bSDmitry Baryshkov MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE), 1689a0dfe9bSDmitry Baryshkov MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE), 1699a0dfe9bSDmitry Baryshkov MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE), 1709a0dfe9bSDmitry Baryshkov {} 1719a0dfe9bSDmitry Baryshkov }; 1729a0dfe9bSDmitry Baryshkov 1739a0dfe9bSDmitry Baryshkov const struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder, 1749a0dfe9bSDmitry Baryshkov const struct drm_display_mode *drm_mode) 1759a0dfe9bSDmitry Baryshkov { 1769a0dfe9bSDmitry Baryshkov struct ch7006_priv *priv = to_ch7006_priv(encoder); 1779a0dfe9bSDmitry Baryshkov const struct ch7006_mode *mode; 1789a0dfe9bSDmitry Baryshkov 1799a0dfe9bSDmitry Baryshkov for (mode = ch7006_modes; mode->mode.clock; mode++) { 1809a0dfe9bSDmitry Baryshkov 1819a0dfe9bSDmitry Baryshkov if (~mode->valid_norms & 1<<priv->norm) 1829a0dfe9bSDmitry Baryshkov continue; 1839a0dfe9bSDmitry Baryshkov 1849a0dfe9bSDmitry Baryshkov if (mode->mode.hdisplay != drm_mode->hdisplay || 1859a0dfe9bSDmitry Baryshkov mode->mode.vdisplay != drm_mode->vdisplay || 1869a0dfe9bSDmitry Baryshkov mode->mode.vtotal != drm_mode->vtotal || 1879a0dfe9bSDmitry Baryshkov mode->mode.htotal != drm_mode->htotal || 1889a0dfe9bSDmitry Baryshkov mode->mode.clock != drm_mode->clock) 1899a0dfe9bSDmitry Baryshkov continue; 1909a0dfe9bSDmitry Baryshkov 1919a0dfe9bSDmitry Baryshkov return mode; 1929a0dfe9bSDmitry Baryshkov } 1939a0dfe9bSDmitry Baryshkov 1949a0dfe9bSDmitry Baryshkov return NULL; 1959a0dfe9bSDmitry Baryshkov } 1969a0dfe9bSDmitry Baryshkov 1979a0dfe9bSDmitry Baryshkov /* Some common HW state calculation code */ 1989a0dfe9bSDmitry Baryshkov 1999a0dfe9bSDmitry Baryshkov void ch7006_setup_levels(struct drm_encoder *encoder) 2009a0dfe9bSDmitry Baryshkov { 201*a7358310SDmitry Baryshkov struct i2c_client *client = nouveau_i2c_encoder_get_client(encoder); 2029a0dfe9bSDmitry Baryshkov struct ch7006_priv *priv = to_ch7006_priv(encoder); 2039a0dfe9bSDmitry Baryshkov uint8_t *regs = priv->state.regs; 2049a0dfe9bSDmitry Baryshkov const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; 2059a0dfe9bSDmitry Baryshkov int gain; 2069a0dfe9bSDmitry Baryshkov int black_level; 2079a0dfe9bSDmitry Baryshkov 2089a0dfe9bSDmitry Baryshkov /* Set DAC_GAIN if the voltage drop between white and black is 2099a0dfe9bSDmitry Baryshkov * high enough. */ 2109a0dfe9bSDmitry Baryshkov if (norm->black_level < 339*fixed1/1000) { 2119a0dfe9bSDmitry Baryshkov gain = 76; 2129a0dfe9bSDmitry Baryshkov 2139a0dfe9bSDmitry Baryshkov regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN; 2149a0dfe9bSDmitry Baryshkov } else { 2159a0dfe9bSDmitry Baryshkov gain = 71; 2169a0dfe9bSDmitry Baryshkov 2179a0dfe9bSDmitry Baryshkov regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN; 2189a0dfe9bSDmitry Baryshkov } 2199a0dfe9bSDmitry Baryshkov 2209a0dfe9bSDmitry Baryshkov black_level = round_fixed(norm->black_level*26625)/gain; 2219a0dfe9bSDmitry Baryshkov 2229a0dfe9bSDmitry Baryshkov /* Correct it with the specified brightness. */ 2239a0dfe9bSDmitry Baryshkov black_level = interpolate(90, black_level, 208, priv->brightness); 2249a0dfe9bSDmitry Baryshkov 2259a0dfe9bSDmitry Baryshkov regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level); 2269a0dfe9bSDmitry Baryshkov 2279a0dfe9bSDmitry Baryshkov ch7006_dbg(client, "black level: %d\n", black_level); 2289a0dfe9bSDmitry Baryshkov } 2299a0dfe9bSDmitry Baryshkov 2309a0dfe9bSDmitry Baryshkov void ch7006_setup_subcarrier(struct drm_encoder *encoder) 2319a0dfe9bSDmitry Baryshkov { 232*a7358310SDmitry Baryshkov struct i2c_client *client = nouveau_i2c_encoder_get_client(encoder); 2339a0dfe9bSDmitry Baryshkov struct ch7006_priv *priv = to_ch7006_priv(encoder); 2349a0dfe9bSDmitry Baryshkov struct ch7006_state *state = &priv->state; 2359a0dfe9bSDmitry Baryshkov const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; 2369a0dfe9bSDmitry Baryshkov const struct ch7006_mode *mode = priv->mode; 2379a0dfe9bSDmitry Baryshkov uint32_t subc_inc; 2389a0dfe9bSDmitry Baryshkov 2399a0dfe9bSDmitry Baryshkov subc_inc = round_fixed((mode->subc_coeff >> 8) 2409a0dfe9bSDmitry Baryshkov * (norm->subc_freq >> 24)); 2419a0dfe9bSDmitry Baryshkov 2429a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC0, 28, subc_inc); 2439a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC1, 24, subc_inc); 2449a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC2, 20, subc_inc); 2459a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC3, 16, subc_inc); 2469a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC4, 12, subc_inc); 2479a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC5, 8, subc_inc); 2489a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC6, 4, subc_inc); 2499a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_SUBC_INC7, 0, subc_inc); 2509a0dfe9bSDmitry Baryshkov 2519a0dfe9bSDmitry Baryshkov ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc); 2529a0dfe9bSDmitry Baryshkov } 2539a0dfe9bSDmitry Baryshkov 2549a0dfe9bSDmitry Baryshkov void ch7006_setup_pll(struct drm_encoder *encoder) 2559a0dfe9bSDmitry Baryshkov { 256*a7358310SDmitry Baryshkov struct i2c_client *client = nouveau_i2c_encoder_get_client(encoder); 2579a0dfe9bSDmitry Baryshkov struct ch7006_priv *priv = to_ch7006_priv(encoder); 2589a0dfe9bSDmitry Baryshkov uint8_t *regs = priv->state.regs; 2599a0dfe9bSDmitry Baryshkov const struct ch7006_mode *mode = priv->mode; 2609a0dfe9bSDmitry Baryshkov int n, best_n = 0; 2619a0dfe9bSDmitry Baryshkov int m, best_m = 0; 2629a0dfe9bSDmitry Baryshkov int freq, best_freq = 0; 2639a0dfe9bSDmitry Baryshkov 2649a0dfe9bSDmitry Baryshkov for (n = 0; n < CH7006_MAXN; n++) { 2659a0dfe9bSDmitry Baryshkov for (m = 0; m < CH7006_MAXM; m++) { 2669a0dfe9bSDmitry Baryshkov freq = CH7006_FREQ0*(n+2)/(m+2); 2679a0dfe9bSDmitry Baryshkov 2689a0dfe9bSDmitry Baryshkov if (abs(freq - mode->mode.clock) < 2699a0dfe9bSDmitry Baryshkov abs(best_freq - mode->mode.clock)) { 2709a0dfe9bSDmitry Baryshkov best_freq = freq; 2719a0dfe9bSDmitry Baryshkov best_n = n; 2729a0dfe9bSDmitry Baryshkov best_m = m; 2739a0dfe9bSDmitry Baryshkov } 2749a0dfe9bSDmitry Baryshkov } 2759a0dfe9bSDmitry Baryshkov } 2769a0dfe9bSDmitry Baryshkov 2779a0dfe9bSDmitry Baryshkov regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) | 2789a0dfe9bSDmitry Baryshkov bitf(CH7006_PLLOV_M_8, best_m); 2799a0dfe9bSDmitry Baryshkov 2809a0dfe9bSDmitry Baryshkov regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m); 2819a0dfe9bSDmitry Baryshkov regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n); 2829a0dfe9bSDmitry Baryshkov 2839a0dfe9bSDmitry Baryshkov if (best_n < 108) 2849a0dfe9bSDmitry Baryshkov regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR; 2859a0dfe9bSDmitry Baryshkov else 2869a0dfe9bSDmitry Baryshkov regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR; 2879a0dfe9bSDmitry Baryshkov 2889a0dfe9bSDmitry Baryshkov ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n", 2899a0dfe9bSDmitry Baryshkov best_n, best_m, best_freq, best_n < 108); 2909a0dfe9bSDmitry Baryshkov } 2919a0dfe9bSDmitry Baryshkov 2929a0dfe9bSDmitry Baryshkov void ch7006_setup_power_state(struct drm_encoder *encoder) 2939a0dfe9bSDmitry Baryshkov { 2949a0dfe9bSDmitry Baryshkov struct ch7006_priv *priv = to_ch7006_priv(encoder); 2959a0dfe9bSDmitry Baryshkov uint8_t *power = &priv->state.regs[CH7006_POWER]; 2969a0dfe9bSDmitry Baryshkov int subconnector; 2979a0dfe9bSDmitry Baryshkov 2989a0dfe9bSDmitry Baryshkov subconnector = priv->select_subconnector ? priv->select_subconnector : 2999a0dfe9bSDmitry Baryshkov priv->subconnector; 3009a0dfe9bSDmitry Baryshkov 3019a0dfe9bSDmitry Baryshkov *power = CH7006_POWER_RESET; 3029a0dfe9bSDmitry Baryshkov 3039a0dfe9bSDmitry Baryshkov if (priv->last_dpms == DRM_MODE_DPMS_ON) { 3049a0dfe9bSDmitry Baryshkov switch (subconnector) { 3059a0dfe9bSDmitry Baryshkov case DRM_MODE_SUBCONNECTOR_SVIDEO: 3069a0dfe9bSDmitry Baryshkov *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF); 3079a0dfe9bSDmitry Baryshkov break; 3089a0dfe9bSDmitry Baryshkov case DRM_MODE_SUBCONNECTOR_Composite: 3099a0dfe9bSDmitry Baryshkov *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF); 3109a0dfe9bSDmitry Baryshkov break; 3119a0dfe9bSDmitry Baryshkov case DRM_MODE_SUBCONNECTOR_SCART: 3129a0dfe9bSDmitry Baryshkov *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) | 3139a0dfe9bSDmitry Baryshkov CH7006_POWER_SCART; 3149a0dfe9bSDmitry Baryshkov break; 3159a0dfe9bSDmitry Baryshkov } 3169a0dfe9bSDmitry Baryshkov 3179a0dfe9bSDmitry Baryshkov } else { 3189a0dfe9bSDmitry Baryshkov if (priv->chip_version >= 0x20) 3199a0dfe9bSDmitry Baryshkov *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF); 3209a0dfe9bSDmitry Baryshkov else 3219a0dfe9bSDmitry Baryshkov *power |= bitfs(CH7006_POWER_LEVEL, POWER_OFF); 3229a0dfe9bSDmitry Baryshkov } 3239a0dfe9bSDmitry Baryshkov } 3249a0dfe9bSDmitry Baryshkov 3259a0dfe9bSDmitry Baryshkov void ch7006_setup_properties(struct drm_encoder *encoder) 3269a0dfe9bSDmitry Baryshkov { 327*a7358310SDmitry Baryshkov struct i2c_client *client = nouveau_i2c_encoder_get_client(encoder); 3289a0dfe9bSDmitry Baryshkov struct ch7006_priv *priv = to_ch7006_priv(encoder); 3299a0dfe9bSDmitry Baryshkov struct ch7006_state *state = &priv->state; 3309a0dfe9bSDmitry Baryshkov const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm]; 3319a0dfe9bSDmitry Baryshkov const struct ch7006_mode *ch_mode = priv->mode; 3329a0dfe9bSDmitry Baryshkov const struct drm_display_mode *mode = &ch_mode->mode; 3339a0dfe9bSDmitry Baryshkov uint8_t *regs = state->regs; 3349a0dfe9bSDmitry Baryshkov int flicker, contrast, hpos, vpos; 3359a0dfe9bSDmitry Baryshkov uint64_t scale, aspect; 3369a0dfe9bSDmitry Baryshkov 3379a0dfe9bSDmitry Baryshkov flicker = interpolate(0, 2, 3, priv->flicker); 3389a0dfe9bSDmitry Baryshkov regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) | 3399a0dfe9bSDmitry Baryshkov bitf(CH7006_FFILTER_LUMA, flicker) | 3409a0dfe9bSDmitry Baryshkov bitf(CH7006_FFILTER_CHROMA, 1); 3419a0dfe9bSDmitry Baryshkov 3429a0dfe9bSDmitry Baryshkov contrast = interpolate(0, 5, 7, priv->contrast); 3439a0dfe9bSDmitry Baryshkov regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast); 3449a0dfe9bSDmitry Baryshkov 3459a0dfe9bSDmitry Baryshkov scale = norm->vtotal*fixed1; 3469a0dfe9bSDmitry Baryshkov do_div(scale, mode->vtotal); 3479a0dfe9bSDmitry Baryshkov 3489a0dfe9bSDmitry Baryshkov aspect = ch_mode->enc_hdisp*fixed1; 3499a0dfe9bSDmitry Baryshkov do_div(aspect, ch_mode->enc_vdisp); 3509a0dfe9bSDmitry Baryshkov 3519a0dfe9bSDmitry Baryshkov hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale) 3529a0dfe9bSDmitry Baryshkov * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4; 3539a0dfe9bSDmitry Baryshkov 3549a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_POV, HPOS_8, hpos); 3559a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_HPOS, 0, hpos); 3569a0dfe9bSDmitry Baryshkov 3579a0dfe9bSDmitry Baryshkov vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale) 3589a0dfe9bSDmitry Baryshkov + norm->voffset) * priv->vmargin / 100 / 2; 3599a0dfe9bSDmitry Baryshkov 3609a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_POV, VPOS_8, vpos); 3619a0dfe9bSDmitry Baryshkov setbitf(state, CH7006_VPOS, 0, vpos); 3629a0dfe9bSDmitry Baryshkov 3639a0dfe9bSDmitry Baryshkov ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos); 3649a0dfe9bSDmitry Baryshkov } 3659a0dfe9bSDmitry Baryshkov 3669a0dfe9bSDmitry Baryshkov /* HW access functions */ 3679a0dfe9bSDmitry Baryshkov 3689a0dfe9bSDmitry Baryshkov void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val) 3699a0dfe9bSDmitry Baryshkov { 3709a0dfe9bSDmitry Baryshkov uint8_t buf[] = {addr, val}; 3719a0dfe9bSDmitry Baryshkov int ret; 3729a0dfe9bSDmitry Baryshkov 3739a0dfe9bSDmitry Baryshkov ret = i2c_master_send(client, buf, ARRAY_SIZE(buf)); 3749a0dfe9bSDmitry Baryshkov if (ret < 0) 3759a0dfe9bSDmitry Baryshkov ch7006_err(client, "Error %d writing to subaddress 0x%x\n", 3769a0dfe9bSDmitry Baryshkov ret, addr); 3779a0dfe9bSDmitry Baryshkov } 3789a0dfe9bSDmitry Baryshkov 3799a0dfe9bSDmitry Baryshkov uint8_t ch7006_read(struct i2c_client *client, uint8_t addr) 3809a0dfe9bSDmitry Baryshkov { 3819a0dfe9bSDmitry Baryshkov uint8_t val; 3829a0dfe9bSDmitry Baryshkov int ret; 3839a0dfe9bSDmitry Baryshkov 3849a0dfe9bSDmitry Baryshkov ret = i2c_master_send(client, &addr, sizeof(addr)); 3859a0dfe9bSDmitry Baryshkov if (ret < 0) 3869a0dfe9bSDmitry Baryshkov goto fail; 3879a0dfe9bSDmitry Baryshkov 3889a0dfe9bSDmitry Baryshkov ret = i2c_master_recv(client, &val, sizeof(val)); 3899a0dfe9bSDmitry Baryshkov if (ret < 0) 3909a0dfe9bSDmitry Baryshkov goto fail; 3919a0dfe9bSDmitry Baryshkov 3929a0dfe9bSDmitry Baryshkov return val; 3939a0dfe9bSDmitry Baryshkov 3949a0dfe9bSDmitry Baryshkov fail: 3959a0dfe9bSDmitry Baryshkov ch7006_err(client, "Error %d reading from subaddress 0x%x\n", 3969a0dfe9bSDmitry Baryshkov ret, addr); 3979a0dfe9bSDmitry Baryshkov return 0; 3989a0dfe9bSDmitry Baryshkov } 3999a0dfe9bSDmitry Baryshkov 4009a0dfe9bSDmitry Baryshkov void ch7006_state_load(struct i2c_client *client, 4019a0dfe9bSDmitry Baryshkov struct ch7006_state *state) 4029a0dfe9bSDmitry Baryshkov { 4039a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_POWER); 4049a0dfe9bSDmitry Baryshkov 4059a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_DISPMODE); 4069a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_FFILTER); 4079a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_BWIDTH); 4089a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_INPUT_FORMAT); 4099a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_CLKMODE); 4109a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_START_ACTIVE); 4119a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_POV); 4129a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_BLACK_LEVEL); 4139a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_HPOS); 4149a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_VPOS); 4159a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_INPUT_SYNC); 4169a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_DETECT); 4179a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_CONTRAST); 4189a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_PLLOV); 4199a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_PLLM); 4209a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_PLLN); 4219a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_BCLKOUT); 4229a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC0); 4239a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC1); 4249a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC2); 4259a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC3); 4269a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC4); 4279a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC5); 4289a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC6); 4299a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_SUBC_INC7); 4309a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_PLL_CONTROL); 4319a0dfe9bSDmitry Baryshkov ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0); 4329a0dfe9bSDmitry Baryshkov } 4339a0dfe9bSDmitry Baryshkov 4349a0dfe9bSDmitry Baryshkov void ch7006_state_save(struct i2c_client *client, 4359a0dfe9bSDmitry Baryshkov struct ch7006_state *state) 4369a0dfe9bSDmitry Baryshkov { 4379a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_POWER); 4389a0dfe9bSDmitry Baryshkov 4399a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_DISPMODE); 4409a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_FFILTER); 4419a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_BWIDTH); 4429a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_INPUT_FORMAT); 4439a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_CLKMODE); 4449a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_START_ACTIVE); 4459a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_POV); 4469a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_BLACK_LEVEL); 4479a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_HPOS); 4489a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_VPOS); 4499a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_INPUT_SYNC); 4509a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_DETECT); 4519a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_CONTRAST); 4529a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_PLLOV); 4539a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_PLLM); 4549a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_PLLN); 4559a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_BCLKOUT); 4569a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC0); 4579a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC1); 4589a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC2); 4599a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC3); 4609a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC4); 4619a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC5); 4629a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC6); 4639a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_SUBC_INC7); 4649a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_PLL_CONTROL); 4659a0dfe9bSDmitry Baryshkov ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0); 4669a0dfe9bSDmitry Baryshkov 4679a0dfe9bSDmitry Baryshkov state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) | 4689a0dfe9bSDmitry Baryshkov (state->regs[CH7006_FFILTER] & 0x0c) >> 2 | 4699a0dfe9bSDmitry Baryshkov (state->regs[CH7006_FFILTER] & 0x03) << 2; 4709a0dfe9bSDmitry Baryshkov } 471