You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
drm/nouveau: Add DRM driver for NVIDIA GPUs
This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA. This driver is a KMS-based driver and requires a compatible nouveau userspace libdrm and nouveau X.org driver. This driver requires firmware files not available in this kernel tree, interested parties can find them via the nouveau project git archive. This driver is reverse engineered, and is in no way supported by nVidia. Support for nearly the complete range of nvidia hw from nv04->g80 (nv50) is available, and the kms driver should support driving nearly all output types (displayport is under development still) along with supporting suspend/resume. This work is all from the upstream nouveau project found at nouveau.freedesktop.org. The original authors list from nouveau git tree is: Anssi Hannula <anssi.hannula@iki.fi> Ben Skeggs <bskeggs@redhat.com> Francisco Jerez <currojerez@riseup.net> Maarten Maathuis <madman2003@gmail.com> Marcin Kościelnicki <koriakin@0x04.net> Matthew Garrett <mjg@redhat.com> Matt Parnell <mparnell@gmail.com> Patrice Mandin <patmandin@gmail.com> Pekka Paalanen <pq@iki.fi> Xavier Chantry <shiningxc@gmail.com> along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
@@ -31,3 +31,5 @@ obj-$(CONFIG_DRM_I915) += i915/
|
||||
obj-$(CONFIG_DRM_SIS) += sis/
|
||||
obj-$(CONFIG_DRM_SAVAGE)+= savage/
|
||||
obj-$(CONFIG_DRM_VIA) +=via/
|
||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||
obj-y += i2c/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
ccflags-y := -Iinclude/drm
|
||||
|
||||
ch7006-y := ch7006_drv.o ch7006_mode.o
|
||||
obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,473 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Francisco Jerez.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "ch7006_priv.h"
|
||||
|
||||
char *ch7006_tv_norm_names[] = {
|
||||
[TV_NORM_PAL] = "PAL",
|
||||
[TV_NORM_PAL_M] = "PAL-M",
|
||||
[TV_NORM_PAL_N] = "PAL-N",
|
||||
[TV_NORM_PAL_NC] = "PAL-Nc",
|
||||
[TV_NORM_PAL_60] = "PAL-60",
|
||||
[TV_NORM_NTSC_M] = "NTSC-M",
|
||||
[TV_NORM_NTSC_J] = "NTSC-J",
|
||||
};
|
||||
|
||||
#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
|
||||
.vdisplay = 480, \
|
||||
.vtotal = 525, \
|
||||
.hvirtual = 660
|
||||
|
||||
#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
|
||||
.vdisplay = 576, \
|
||||
.vtotal = 625, \
|
||||
.hvirtual = 810
|
||||
|
||||
struct ch7006_tv_norm_info ch7006_tv_norms[] = {
|
||||
[TV_NORM_NTSC_M] = {
|
||||
NTSC_LIKE_TIMINGS,
|
||||
.black_level = 0.339 * fixed1,
|
||||
.subc_freq = 3579545 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
|
||||
.voffset = 0,
|
||||
},
|
||||
[TV_NORM_NTSC_J] = {
|
||||
NTSC_LIKE_TIMINGS,
|
||||
.black_level = 0.286 * fixed1,
|
||||
.subc_freq = 3579545 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
|
||||
.voffset = 0,
|
||||
},
|
||||
[TV_NORM_PAL] = {
|
||||
PAL_LIKE_TIMINGS,
|
||||
.black_level = 0.3 * fixed1,
|
||||
.subc_freq = 4433618.75 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
|
||||
.voffset = 0,
|
||||
},
|
||||
[TV_NORM_PAL_M] = {
|
||||
NTSC_LIKE_TIMINGS,
|
||||
.black_level = 0.339 * fixed1,
|
||||
.subc_freq = 3575611.433 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
|
||||
.voffset = 16,
|
||||
},
|
||||
|
||||
/* The following modes seem to work right but they're
|
||||
* undocumented */
|
||||
|
||||
[TV_NORM_PAL_N] = {
|
||||
PAL_LIKE_TIMINGS,
|
||||
.black_level = 0.339 * fixed1,
|
||||
.subc_freq = 4433618.75 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
|
||||
.voffset = 0,
|
||||
},
|
||||
[TV_NORM_PAL_NC] = {
|
||||
PAL_LIKE_TIMINGS,
|
||||
.black_level = 0.3 * fixed1,
|
||||
.subc_freq = 3582056.25 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
|
||||
.voffset = 0,
|
||||
},
|
||||
[TV_NORM_PAL_60] = {
|
||||
NTSC_LIKE_TIMINGS,
|
||||
.black_level = 0.3 * fixed1,
|
||||
.subc_freq = 4433618.75 * fixed1,
|
||||
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
|
||||
.voffset = 16,
|
||||
},
|
||||
};
|
||||
|
||||
#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
|
||||
subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
|
||||
.mode = { \
|
||||
.name = #hd "x" #vd, \
|
||||
.status = 0, \
|
||||
.type = DRM_MODE_TYPE_DRIVER, \
|
||||
.clock = f, \
|
||||
.hdisplay = hd, \
|
||||
.hsync_start = e_hd + 16, \
|
||||
.hsync_end = e_hd + 80, \
|
||||
.htotal = ht, \
|
||||
.hskew = 0, \
|
||||
.vdisplay = vd, \
|
||||
.vsync_start = vd + 10, \
|
||||
.vsync_end = vd + 26, \
|
||||
.vtotal = vt, \
|
||||
.vscan = 0, \
|
||||
.flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
|
||||
DRM_MODE_FLAG_##vsynp##VSYNC, \
|
||||
.vrefresh = 0, \
|
||||
}, \
|
||||
.enc_hdisp = e_hd, \
|
||||
.enc_vdisp = e_vd, \
|
||||
.subc_coeff = subc * fixed1, \
|
||||
.dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
|
||||
bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
|
||||
.valid_scales = scale_mask, \
|
||||
.valid_norms = norm_mask \
|
||||
}
|
||||
|
||||
#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
|
||||
subc, scale, scale_mask, norm_mask) \
|
||||
__MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
|
||||
scale_mask, norm_mask, hd, vd)
|
||||
|
||||
#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
|
||||
1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
|
||||
|
||||
#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
|
||||
|
||||
struct ch7006_mode ch7006_modes[] = {
|
||||
MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
|
||||
MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
|
||||
MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
|
||||
MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
|
||||
MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
|
||||
MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
|
||||
MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
|
||||
MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
|
||||
MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
|
||||
MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
|
||||
MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
|
||||
MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
|
||||
MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
|
||||
MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
|
||||
MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
|
||||
MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
|
||||
MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
|
||||
MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
|
||||
MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
|
||||
__MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
|
||||
MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
|
||||
MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
|
||||
MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
|
||||
MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
|
||||
MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
|
||||
{}
|
||||
};
|
||||
|
||||
struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *drm_mode)
|
||||
{
|
||||
struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
||||
struct ch7006_mode *mode;
|
||||
|
||||
for (mode = ch7006_modes; mode->mode.clock; mode++) {
|
||||
|
||||
if (~mode->valid_norms & 1<<priv->norm)
|
||||
continue;
|
||||
|
||||
if (mode->mode.hdisplay != drm_mode->hdisplay ||
|
||||
mode->mode.vdisplay != drm_mode->vdisplay ||
|
||||
mode->mode.vtotal != drm_mode->vtotal ||
|
||||
mode->mode.htotal != drm_mode->htotal ||
|
||||
mode->mode.clock != drm_mode->clock)
|
||||
continue;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Some common HW state calculation code */
|
||||
|
||||
void ch7006_setup_levels(struct drm_encoder *encoder)
|
||||
{
|
||||
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
||||
struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
||||
uint8_t *regs = priv->state.regs;
|
||||
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
||||
int gain;
|
||||
int black_level;
|
||||
|
||||
/* Set DAC_GAIN if the voltage drop between white and black is
|
||||
* high enough. */
|
||||
if (norm->black_level < 339*fixed1/1000) {
|
||||
gain = 76;
|
||||
|
||||
regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
|
||||
} else {
|
||||
gain = 71;
|
||||
|
||||
regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
|
||||
}
|
||||
|
||||
black_level = round_fixed(norm->black_level*26625)/gain;
|
||||
|
||||
/* Correct it with the specified brightness. */
|
||||
black_level = interpolate(90, black_level, 208, priv->brightness);
|
||||
|
||||
regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
|
||||
|
||||
ch7006_dbg(client, "black level: %d\n", black_level);
|
||||
}
|
||||
|
||||
void ch7006_setup_subcarrier(struct drm_encoder *encoder)
|
||||
{
|
||||
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
||||
struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
||||
struct ch7006_state *state = &priv->state;
|
||||
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
||||
struct ch7006_mode *mode = priv->mode;
|
||||
uint32_t subc_inc;
|
||||
|
||||
subc_inc = round_fixed((mode->subc_coeff >> 8)
|
||||
* (norm->subc_freq >> 24));
|
||||
|
||||
setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
|
||||
setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
|
||||
|
||||
ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
|
||||
}
|
||||
|
||||
void ch7006_setup_pll(struct drm_encoder *encoder)
|
||||
{
|
||||
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
||||
struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
||||
uint8_t *regs = priv->state.regs;
|
||||
struct ch7006_mode *mode = priv->mode;
|
||||
int n, best_n = 0;
|
||||
int m, best_m = 0;
|
||||
int freq, best_freq = 0;
|
||||
|
||||
for (n = 0; n < CH7006_MAXN; n++) {
|
||||
for (m = 0; m < CH7006_MAXM; m++) {
|
||||
freq = CH7006_FREQ0*(n+2)/(m+2);
|
||||
|
||||
if (abs(freq - mode->mode.clock) <
|
||||
abs(best_freq - mode->mode.clock)) {
|
||||
best_freq = freq;
|
||||
best_n = n;
|
||||
best_m = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
|
||||
bitf(CH7006_PLLOV_M_8, best_m);
|
||||
|
||||
regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
|
||||
regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
|
||||
|
||||
if (best_n < 108)
|
||||
regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
|
||||
else
|
||||
regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
|
||||
|
||||
ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
|
||||
best_n, best_m, best_freq, best_n < 108);
|
||||
}
|
||||
|
||||
void ch7006_setup_power_state(struct drm_encoder *encoder)
|
||||
{
|
||||
struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
||||
uint8_t *power = &priv->state.regs[CH7006_POWER];
|
||||
int subconnector;
|
||||
|
||||
subconnector = priv->select_subconnector ? priv->select_subconnector :
|
||||
priv->subconnector;
|
||||
|
||||
*power = CH7006_POWER_RESET;
|
||||
|
||||
if (priv->last_dpms == DRM_MODE_DPMS_ON) {
|
||||
switch (subconnector) {
|
||||
case DRM_MODE_SUBCONNECTOR_SVIDEO:
|
||||
*power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
|
||||
break;
|
||||
case DRM_MODE_SUBCONNECTOR_Composite:
|
||||
*power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
|
||||
break;
|
||||
case DRM_MODE_SUBCONNECTOR_SCART:
|
||||
*power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
|
||||
CH7006_POWER_SCART;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
*power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
void ch7006_setup_properties(struct drm_encoder *encoder)
|
||||
{
|
||||
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
|
||||
struct ch7006_priv *priv = to_ch7006_priv(encoder);
|
||||
struct ch7006_state *state = &priv->state;
|
||||
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
|
||||
struct ch7006_mode *ch_mode = priv->mode;
|
||||
struct drm_display_mode *mode = &ch_mode->mode;
|
||||
uint8_t *regs = state->regs;
|
||||
int flicker, contrast, hpos, vpos;
|
||||
uint64_t scale, aspect;
|
||||
|
||||
flicker = interpolate(0, 2, 3, priv->flicker);
|
||||
regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
|
||||
bitf(CH7006_FFILTER_LUMA, flicker) |
|
||||
bitf(CH7006_FFILTER_CHROMA, 1);
|
||||
|
||||
contrast = interpolate(0, 5, 7, priv->contrast);
|
||||
regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
|
||||
|
||||
scale = norm->vtotal*fixed1;
|
||||
do_div(scale, mode->vtotal);
|
||||
|
||||
aspect = ch_mode->enc_hdisp*fixed1;
|
||||
do_div(aspect, ch_mode->enc_vdisp);
|
||||
|
||||
hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
|
||||
* priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
|
||||
|
||||
setbitf(state, CH7006_POV, HPOS_8, hpos);
|
||||
setbitf(state, CH7006_HPOS, 0, hpos);
|
||||
|
||||
vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
|
||||
+ norm->voffset) * priv->vmargin / 100 / 2;
|
||||
|
||||
setbitf(state, CH7006_POV, VPOS_8, vpos);
|
||||
setbitf(state, CH7006_VPOS, 0, vpos);
|
||||
|
||||
ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
|
||||
}
|
||||
|
||||
/* HW access functions */
|
||||
|
||||
void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
|
||||
{
|
||||
uint8_t buf[] = {addr, val};
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
|
||||
if (ret < 0)
|
||||
ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
|
||||
ret, addr);
|
||||
}
|
||||
|
||||
uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
|
||||
{
|
||||
uint8_t val;
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, &addr, sizeof(addr));
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = i2c_master_recv(client, &val, sizeof(val));
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
return val;
|
||||
|
||||
fail:
|
||||
ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
|
||||
ret, addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ch7006_state_load(struct i2c_client *client,
|
||||
struct ch7006_state *state)
|
||||
{
|
||||
ch7006_load_reg(client, state, CH7006_POWER);
|
||||
|
||||
ch7006_load_reg(client, state, CH7006_DISPMODE);
|
||||
ch7006_load_reg(client, state, CH7006_FFILTER);
|
||||
ch7006_load_reg(client, state, CH7006_BWIDTH);
|
||||
ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
|
||||
ch7006_load_reg(client, state, CH7006_CLKMODE);
|
||||
ch7006_load_reg(client, state, CH7006_START_ACTIVE);
|
||||
ch7006_load_reg(client, state, CH7006_POV);
|
||||
ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
|
||||
ch7006_load_reg(client, state, CH7006_HPOS);
|
||||
ch7006_load_reg(client, state, CH7006_VPOS);
|
||||
ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
|
||||
ch7006_load_reg(client, state, CH7006_DETECT);
|
||||
ch7006_load_reg(client, state, CH7006_CONTRAST);
|
||||
ch7006_load_reg(client, state, CH7006_PLLOV);
|
||||
ch7006_load_reg(client, state, CH7006_PLLM);
|
||||
ch7006_load_reg(client, state, CH7006_PLLN);
|
||||
ch7006_load_reg(client, state, CH7006_BCLKOUT);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC0);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC1);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC2);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC3);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC4);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC5);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC6);
|
||||
ch7006_load_reg(client, state, CH7006_SUBC_INC7);
|
||||
ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
|
||||
ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
|
||||
|
||||
/* I don't know what this is for, but otherwise I get no
|
||||
* signal.
|
||||
*/
|
||||
ch7006_write(client, 0x3d, 0x0);
|
||||
}
|
||||
|
||||
void ch7006_state_save(struct i2c_client *client,
|
||||
struct ch7006_state *state)
|
||||
{
|
||||
ch7006_save_reg(client, state, CH7006_POWER);
|
||||
|
||||
ch7006_save_reg(client, state, CH7006_DISPMODE);
|
||||
ch7006_save_reg(client, state, CH7006_FFILTER);
|
||||
ch7006_save_reg(client, state, CH7006_BWIDTH);
|
||||
ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
|
||||
ch7006_save_reg(client, state, CH7006_CLKMODE);
|
||||
ch7006_save_reg(client, state, CH7006_START_ACTIVE);
|
||||
ch7006_save_reg(client, state, CH7006_POV);
|
||||
ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
|
||||
ch7006_save_reg(client, state, CH7006_HPOS);
|
||||
ch7006_save_reg(client, state, CH7006_VPOS);
|
||||
ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
|
||||
ch7006_save_reg(client, state, CH7006_DETECT);
|
||||
ch7006_save_reg(client, state, CH7006_CONTRAST);
|
||||
ch7006_save_reg(client, state, CH7006_PLLOV);
|
||||
ch7006_save_reg(client, state, CH7006_PLLM);
|
||||
ch7006_save_reg(client, state, CH7006_PLLN);
|
||||
ch7006_save_reg(client, state, CH7006_BCLKOUT);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC0);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC1);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC2);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC3);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC4);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC5);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC6);
|
||||
ch7006_save_reg(client, state, CH7006_SUBC_INC7);
|
||||
ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
|
||||
ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
|
||||
|
||||
state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
|
||||
(state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
|
||||
(state->regs[CH7006_FFILTER] & 0x03) << 2;
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Francisco Jerez.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRM_I2C_CH7006_PRIV_H__
|
||||
#define __DRM_I2C_CH7006_PRIV_H__
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "drm_encoder_slave.h"
|
||||
#include "i2c/ch7006.h"
|
||||
|
||||
typedef int64_t fixed;
|
||||
#define fixed1 (1LL << 32)
|
||||
|
||||
enum ch7006_tv_norm {
|
||||
TV_NORM_PAL,
|
||||
TV_NORM_PAL_M,
|
||||
TV_NORM_PAL_N,
|
||||
TV_NORM_PAL_NC,
|
||||
TV_NORM_PAL_60,
|
||||
TV_NORM_NTSC_M,
|
||||
TV_NORM_NTSC_J,
|
||||
NUM_TV_NORMS
|
||||
};
|
||||
|
||||
struct ch7006_tv_norm_info {
|
||||
fixed vrefresh;
|
||||
int vdisplay;
|
||||
int vtotal;
|
||||
int hvirtual;
|
||||
|
||||
fixed subc_freq;
|
||||
fixed black_level;
|
||||
|
||||
uint32_t dispmode;
|
||||
int voffset;
|
||||
};
|
||||
|
||||
struct ch7006_mode {
|
||||
struct drm_display_mode mode;
|
||||
|
||||
int enc_hdisp;
|
||||
int enc_vdisp;
|
||||
|
||||
fixed subc_coeff;
|
||||
uint32_t dispmode;
|
||||
|
||||
uint32_t valid_scales;
|
||||
uint32_t valid_norms;
|
||||
};
|
||||
|
||||
struct ch7006_state {
|
||||
uint8_t regs[0x26];
|
||||
};
|
||||
|
||||
struct ch7006_priv {
|
||||
struct ch7006_encoder_params *params;
|
||||
struct ch7006_mode *mode;
|
||||
|
||||
struct ch7006_state state;
|
||||
struct ch7006_state saved_state;
|
||||
|
||||
struct drm_property *scale_property;
|
||||
|
||||
int select_subconnector;
|
||||
int subconnector;
|
||||
int hmargin;
|
||||
int vmargin;
|
||||
enum ch7006_tv_norm norm;
|
||||
int brightness;
|
||||
int contrast;
|
||||
int flicker;
|
||||
int scale;
|
||||
|
||||
int last_dpms;
|
||||
};
|
||||
|
||||
#define to_ch7006_priv(x) \
|
||||
((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
|
||||
|
||||
extern int ch7006_debug;
|
||||
extern char *ch7006_tv_norm;
|
||||
extern int ch7006_scale;
|
||||
|
||||
extern char *ch7006_tv_norm_names[];
|
||||
extern struct ch7006_tv_norm_info ch7006_tv_norms[];
|
||||
extern struct ch7006_mode ch7006_modes[];
|
||||
|
||||
struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *drm_mode);
|
||||
|
||||
void ch7006_setup_levels(struct drm_encoder *encoder);
|
||||
void ch7006_setup_subcarrier(struct drm_encoder *encoder);
|
||||
void ch7006_setup_pll(struct drm_encoder *encoder);
|
||||
void ch7006_setup_power_state(struct drm_encoder *encoder);
|
||||
void ch7006_setup_properties(struct drm_encoder *encoder);
|
||||
|
||||
void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
|
||||
uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
|
||||
|
||||
void ch7006_state_load(struct i2c_client *client,
|
||||
struct ch7006_state *state);
|
||||
void ch7006_state_save(struct i2c_client *client,
|
||||
struct ch7006_state *state);
|
||||
|
||||
/* Some helper macros */
|
||||
|
||||
#define ch7006_dbg(client, format, ...) do { \
|
||||
if (ch7006_debug) \
|
||||
dev_printk(KERN_DEBUG, &client->dev, \
|
||||
"%s: " format, __func__, ## __VA_ARGS__); \
|
||||
} while (0)
|
||||
#define ch7006_info(client, format, ...) \
|
||||
dev_info(&client->dev, format, __VA_ARGS__)
|
||||
#define ch7006_err(client, format, ...) \
|
||||
dev_err(&client->dev, format, __VA_ARGS__)
|
||||
|
||||
#define __mask(src, bitfield) \
|
||||
(((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
|
||||
#define mask(bitfield) __mask(bitfield)
|
||||
|
||||
#define __bitf(src, bitfield, x) \
|
||||
(((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield))
|
||||
#define bitf(bitfield, x) __bitf(bitfield, x)
|
||||
#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
|
||||
#define setbitf(state, reg, bitfield, x) \
|
||||
state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \
|
||||
| bitf(reg##_##bitfield, x)
|
||||
|
||||
#define __unbitf(src, bitfield, x) \
|
||||
((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
|
||||
#define unbitf(bitfield, x) __unbitf(bitfield, x)
|
||||
|
||||
static inline int interpolate(int y0, int y1, int y2, int x)
|
||||
{
|
||||
return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
|
||||
}
|
||||
|
||||
static inline int32_t round_fixed(fixed x)
|
||||
{
|
||||
return (x + fixed1/2) >> 32;
|
||||
}
|
||||
|
||||
#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
|
||||
#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
|
||||
|
||||
/* Fixed hardware specs */
|
||||
|
||||
#define CH7006_FREQ0 14318
|
||||
#define CH7006_MAXN 650
|
||||
#define CH7006_MAXM 315
|
||||
|
||||
/* Register definitions */
|
||||
|
||||
#define CH7006_DISPMODE 0x00
|
||||
#define CH7006_DISPMODE_INPUT_RES 0, 7:5
|
||||
#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
|
||||
#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
|
||||
#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
|
||||
#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
|
||||
#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
|
||||
#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
|
||||
#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
|
||||
#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
|
||||
#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
|
||||
#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
|
||||
#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
|
||||
#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
|
||||
#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
|
||||
#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
|
||||
#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
|
||||
#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
|
||||
#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
|
||||
#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
|
||||
|
||||
#define CH7006_FFILTER 0x01
|
||||
#define CH7006_FFILTER_TEXT 0, 5:4
|
||||
#define CH7006_FFILTER_LUMA 0, 3:2
|
||||
#define CH7006_FFILTER_CHROMA 0, 1:0
|
||||
#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
|
||||
|
||||
#define CH7006_BWIDTH 0x03
|
||||
#define CH7006_BWIDTH_5L_FFILER (1 << 7)
|
||||
#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
|
||||
#define CH7006_BWIDTH_CHROMA 0, 5:4
|
||||
#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
|
||||
#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
|
||||
#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
|
||||
|
||||
#define CH7006_INPUT_FORMAT 0x04
|
||||
#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
|
||||
#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
|
||||
#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
|
||||
#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
|
||||
|
||||
#define CH7006_CLKMODE 0x06
|
||||
#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
|
||||
#define CH7006_CLKMODE_MASTER (1 << 6)
|
||||
#define CH7006_CLKMODE_POS_EDGE (1 << 4)
|
||||
#define CH7006_CLKMODE_XCM 0, 3:2
|
||||
#define CH7006_CLKMODE_PCM 0, 1:0
|
||||
|
||||
#define CH7006_START_ACTIVE 0x07
|
||||
#define CH7006_START_ACTIVE_0 0, 7:0
|
||||
|
||||
#define CH7006_POV 0x08
|
||||
#define CH7006_POV_START_ACTIVE_8 8, 2:2
|
||||
#define CH7006_POV_HPOS_8 8, 1:1
|
||||
#define CH7006_POV_VPOS_8 8, 0:0
|
||||
|
||||
#define CH7006_BLACK_LEVEL 0x09
|
||||
#define CH7006_BLACK_LEVEL_0 0, 7:0
|
||||
|
||||
#define CH7006_HPOS 0x0a
|
||||
#define CH7006_HPOS_0 0, 7:0
|
||||
|
||||
#define CH7006_VPOS 0x0b
|
||||
#define CH7006_VPOS_0 0, 7:0
|
||||
|
||||
#define CH7006_INPUT_SYNC 0x0d
|
||||
#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
|
||||
#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
|
||||
#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
|
||||
#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
|
||||
|
||||
#define CH7006_POWER 0x0e
|
||||
#define CH7006_POWER_SCART (1 << 4)
|
||||
#define CH7006_POWER_RESET (1 << 3)
|
||||
#define CH7006_POWER_LEVEL 0, 2:0
|
||||
#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
|
||||
#define CH7006_POWER_LEVEL_POWER_OFF 0x1
|
||||
#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
|
||||
#define CH7006_POWER_LEVEL_NORMAL 0x3
|
||||
#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
|
||||
|
||||
#define CH7006_DETECT 0x10
|
||||
#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
|
||||
#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
|
||||
#define CH7006_DETECT_CVBS_TEST (1 << 1)
|
||||
#define CH7006_DETECT_SENSE (1 << 0)
|
||||
|
||||
#define CH7006_CONTRAST 0x11
|
||||
#define CH7006_CONTRAST_0 0, 2:0
|
||||
|
||||
#define CH7006_PLLOV 0x13
|
||||
#define CH7006_PLLOV_N_8 8, 2:1
|
||||
#define CH7006_PLLOV_M_8 8, 0:0
|
||||
|
||||
#define CH7006_PLLM 0x14
|
||||
#define CH7006_PLLM_0 0, 7:0
|
||||
|
||||
#define CH7006_PLLN 0x15
|
||||
#define CH7006_PLLN_0 0, 7:0
|
||||
|
||||
#define CH7006_BCLKOUT 0x17
|
||||
|
||||
#define CH7006_SUBC_INC0 0x18
|
||||
#define CH7006_SUBC_INC0_28 28, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC1 0x19
|
||||
#define CH7006_SUBC_INC1_24 24, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC2 0x1a
|
||||
#define CH7006_SUBC_INC2_20 20, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC3 0x1b
|
||||
#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
|
||||
#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
|
||||
#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
|
||||
#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
|
||||
#define CH7006_SUBC_INC3_16 16, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC4 0x1c
|
||||
#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
|
||||
#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
|
||||
#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
|
||||
#define CH7006_SUBC_INC4_12 12, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC5 0x1d
|
||||
#define CH7006_SUBC_INC5_8 8, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC6 0x1e
|
||||
#define CH7006_SUBC_INC6_4 4, 3:0
|
||||
|
||||
#define CH7006_SUBC_INC7 0x1f
|
||||
#define CH7006_SUBC_INC7_0 0, 3:0
|
||||
|
||||
#define CH7006_PLL_CONTROL 0x20
|
||||
#define CH7006_PLL_CONTROL_CPI (1 << 5)
|
||||
#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
|
||||
#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
|
||||
#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
|
||||
#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
|
||||
#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
|
||||
|
||||
#define CH7006_CALC_SUBC_INC0 0x21
|
||||
#define CH7006_CALC_SUBC_INC0_24 24, 4:3
|
||||
#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
|
||||
#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
|
||||
|
||||
#define CH7006_CALC_SUBC_INC1 0x22
|
||||
#define CH7006_CALC_SUBC_INC1_16 16, 7:0
|
||||
|
||||
#define CH7006_CALC_SUBC_INC2 0x23
|
||||
#define CH7006_CALC_SUBC_INC2_8 8, 7:0
|
||||
|
||||
#define CH7006_CALC_SUBC_INC3 0x24
|
||||
#define CH7006_CALC_SUBC_INC3_0 0, 7:0
|
||||
|
||||
#define CH7006_VERSION_ID 0x25
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
config DRM_NOUVEAU
|
||||
tristate "Nouveau (nVidia) cards"
|
||||
depends on DRM
|
||||
select FW_LOADER
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_TTM
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select FB
|
||||
select FRAMEBUFFER_CONSOLE if !EMBEDDED
|
||||
select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
|
||||
help
|
||||
Choose this option for open-source nVidia support.
|
||||
|
||||
config DRM_NOUVEAU_BACKLIGHT
|
||||
bool "Support for backlight control"
|
||||
depends on DRM_NOUVEAU
|
||||
default y
|
||||
help
|
||||
Say Y here if you want to control the backlight of your display
|
||||
(e.g. a laptop panel).
|
||||
|
||||
config DRM_NOUVEAU_DEBUG
|
||||
bool "Build in Nouveau's debugfs support"
|
||||
depends on DRM_NOUVEAU && DEBUG_FS
|
||||
default y
|
||||
help
|
||||
Say Y here if you want Nouveau to output debugging information
|
||||
via debugfs.
|
||||
|
||||
menu "I2C encoder or helper chips"
|
||||
depends on DRM
|
||||
|
||||
config DRM_I2C_CH7006
|
||||
tristate "Chrontel ch7006 TV encoder"
|
||||
default m if DRM_NOUVEAU
|
||||
help
|
||||
Support for Chrontel ch7006 and similar TV encoders, found
|
||||
on some nVidia video cards.
|
||||
|
||||
This driver is currently only useful if you're also using
|
||||
the nouveau driver.
|
||||
endmenu
|
||||
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# Makefile for the drm device driver. This driver provides support for the
|
||||
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
||||
|
||||
ccflags-y := -Iinclude/drm
|
||||
nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
|
||||
nouveau_object.o nouveau_irq.o nouveau_notifier.o \
|
||||
nouveau_sgdma.o nouveau_dma.o \
|
||||
nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
|
||||
nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
|
||||
nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
|
||||
nouveau_dp.o \
|
||||
nv04_timer.o \
|
||||
nv04_mc.o nv40_mc.o nv50_mc.o \
|
||||
nv04_fb.o nv10_fb.o nv40_fb.o \
|
||||
nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
|
||||
nv04_graph.o nv10_graph.o nv20_graph.o \
|
||||
nv40_graph.o nv50_graph.o \
|
||||
nv04_instmem.o nv50_instmem.o \
|
||||
nv50_crtc.o nv50_dac.o nv50_sor.o \
|
||||
nv50_cursor.o nv50_display.o nv50_fbcon.o \
|
||||
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
|
||||
nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
|
||||
nv17_gpio.o
|
||||
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
|
||||
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
|
||||
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
|
||||
|
||||
obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
|
||||
@@ -0,0 +1,125 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm.h"
|
||||
#include "drm_sarea.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nv50_display.h"
|
||||
|
||||
#define NOUVEAU_DSM_SUPPORTED 0x00
|
||||
#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
|
||||
|
||||
#define NOUVEAU_DSM_ACTIVE 0x01
|
||||
#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
|
||||
|
||||
#define NOUVEAU_DSM_LED 0x02
|
||||
#define NOUVEAU_DSM_LED_STATE 0x00
|
||||
#define NOUVEAU_DSM_LED_OFF 0x10
|
||||
#define NOUVEAU_DSM_LED_STAMINA 0x11
|
||||
#define NOUVEAU_DSM_LED_SPEED 0x12
|
||||
|
||||
#define NOUVEAU_DSM_POWER 0x03
|
||||
#define NOUVEAU_DSM_POWER_STATE 0x00
|
||||
#define NOUVEAU_DSM_POWER_SPEED 0x01
|
||||
#define NOUVEAU_DSM_POWER_STAMINA 0x02
|
||||
|
||||
static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
|
||||
{
|
||||
static char muid[] = {
|
||||
0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
|
||||
0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
|
||||
};
|
||||
|
||||
struct pci_dev *pdev = dev->pdev;
|
||||
struct acpi_handle *handle;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
struct acpi_object_list input;
|
||||
union acpi_object params[4];
|
||||
union acpi_object *obj;
|
||||
int err;
|
||||
|
||||
handle = DEVICE_ACPI_HANDLE(&pdev->dev);
|
||||
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
input.count = 4;
|
||||
input.pointer = params;
|
||||
params[0].type = ACPI_TYPE_BUFFER;
|
||||
params[0].buffer.length = sizeof(muid);
|
||||
params[0].buffer.pointer = (char *)muid;
|
||||
params[1].type = ACPI_TYPE_INTEGER;
|
||||
params[1].integer.value = 0x00000102;
|
||||
params[2].type = ACPI_TYPE_INTEGER;
|
||||
params[2].integer.value = func;
|
||||
params[3].type = ACPI_TYPE_INTEGER;
|
||||
params[3].integer.value = arg;
|
||||
|
||||
err = acpi_evaluate_object(handle, "_DSM", &input, &output);
|
||||
if (err) {
|
||||
NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
|
||||
if (obj->type == ACPI_TYPE_INTEGER)
|
||||
if (obj->integer.value == 0x80000002)
|
||||
return -ENODEV;
|
||||
|
||||
if (obj->type == ACPI_TYPE_BUFFER) {
|
||||
if (obj->buffer.length == 4 && result) {
|
||||
*result = 0;
|
||||
*result |= obj->buffer.pointer[0];
|
||||
*result |= (obj->buffer.pointer[1] << 8);
|
||||
*result |= (obj->buffer.pointer[2] << 16);
|
||||
*result |= (obj->buffer.pointer[3] << 24);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(output.pointer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nouveau_hybrid_setup(struct drm_device *dev)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (nouveau_dsm(dev, NOUVEAU_DSM_ACTIVE, NOUVEAU_DSM_ACTIVE_QUERY,
|
||||
&result))
|
||||
return -ENODEV;
|
||||
|
||||
NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
|
||||
|
||||
if (result & 0x1) { /* Stamina mode - disable the external GPU */
|
||||
nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
|
||||
NULL);
|
||||
nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
|
||||
NULL);
|
||||
} else { /* Ensure that the external GPU is enabled */
|
||||
nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
|
||||
nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool nouveau_dsm_probe(struct drm_device *dev)
|
||||
{
|
||||
int support = 0;
|
||||
|
||||
if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
|
||||
NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
|
||||
return false;
|
||||
|
||||
if (!support)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Authors:
|
||||
* Matthew Garrett <mjg@redhat.com>
|
||||
*
|
||||
* Register locations derived from NVClock by Roderick Colenbrander
|
||||
*/
|
||||
|
||||
#include <linux/backlight.h>
|
||||
|
||||
#include "drmP.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_reg.h"
|
||||
|
||||
static int nv40_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(bd);
|
||||
int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
|
||||
>> 16;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int nv40_set_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(bd);
|
||||
int val = bd->props.brightness;
|
||||
int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT);
|
||||
|
||||
nv_wr32(dev, NV40_PMC_BACKLIGHT,
|
||||
(val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct backlight_ops nv40_bl_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.get_brightness = nv40_get_intensity,
|
||||
.update_status = nv40_set_intensity,
|
||||
};
|
||||
|
||||
static int nv50_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(bd);
|
||||
|
||||
return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
|
||||
}
|
||||
|
||||
static int nv50_set_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(bd);
|
||||
int val = bd->props.brightness;
|
||||
|
||||
nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
|
||||
val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct backlight_ops nv50_bl_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.get_brightness = nv50_get_intensity,
|
||||
.update_status = nv50_set_intensity,
|
||||
};
|
||||
|
||||
static int nouveau_nv40_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct backlight_device *bd;
|
||||
|
||||
if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
|
||||
return 0;
|
||||
|
||||
bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
|
||||
&nv40_bl_ops);
|
||||
if (IS_ERR(bd))
|
||||
return PTR_ERR(bd);
|
||||
|
||||
dev_priv->backlight = bd;
|
||||
bd->props.max_brightness = 31;
|
||||
bd->props.brightness = nv40_get_intensity(bd);
|
||||
backlight_update_status(bd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nouveau_nv50_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct backlight_device *bd;
|
||||
|
||||
if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
|
||||
return 0;
|
||||
|
||||
bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
|
||||
&nv50_bl_ops);
|
||||
if (IS_ERR(bd))
|
||||
return PTR_ERR(bd);
|
||||
|
||||
dev_priv->backlight = bd;
|
||||
bd->props.max_brightness = 1025;
|
||||
bd->props.brightness = nv50_get_intensity(bd);
|
||||
backlight_update_status(bd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nouveau_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
|
||||
switch (dev_priv->card_type) {
|
||||
case NV_40:
|
||||
return nouveau_nv40_backlight_init(dev);
|
||||
case NV_50:
|
||||
return nouveau_nv50_backlight_init(dev);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nouveau_backlight_exit(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (dev_priv->backlight) {
|
||||
backlight_device_unregister(dev_priv->backlight);
|
||||
dev_priv->backlight = NULL;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2007-2008 Nouveau Project
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __NOUVEAU_BIOS_H__
|
||||
#define __NOUVEAU_BIOS_H__
|
||||
|
||||
#include "nvreg.h"
|
||||
#include "nouveau_i2c.h"
|
||||
|
||||
#define DCB_MAX_NUM_ENTRIES 16
|
||||
#define DCB_MAX_NUM_I2C_ENTRIES 16
|
||||
#define DCB_MAX_NUM_GPIO_ENTRIES 32
|
||||
#define DCB_MAX_NUM_CONNECTOR_ENTRIES 16
|
||||
|
||||
#define DCB_LOC_ON_CHIP 0
|
||||
|
||||
struct dcb_entry {
|
||||
int index; /* may not be raw dcb index if merging has happened */
|
||||
uint8_t type;
|
||||
uint8_t i2c_index;
|
||||
uint8_t heads;
|
||||
uint8_t connector;
|
||||
uint8_t bus;
|
||||
uint8_t location;
|
||||
uint8_t or;
|
||||
bool duallink_possible;
|
||||
union {
|
||||
struct sor_conf {
|
||||
int link;
|
||||
} sorconf;
|
||||
struct {
|
||||
int maxfreq;
|
||||
} crtconf;
|
||||
struct {
|
||||
struct sor_conf sor;
|
||||
bool use_straps_for_mode;
|
||||
bool use_power_scripts;
|
||||
} lvdsconf;
|
||||
struct {
|
||||
bool has_component_output;
|
||||
} tvconf;
|
||||
struct {
|
||||
struct sor_conf sor;
|
||||
int link_nr;
|
||||
int link_bw;
|
||||
} dpconf;
|
||||
struct {
|
||||
struct sor_conf sor;
|
||||
} tmdsconf;
|
||||
};
|
||||
bool i2c_upper_default;
|
||||
};
|
||||
|
||||
struct dcb_i2c_entry {
|
||||
uint8_t port_type;
|
||||
uint8_t read, write;
|
||||
struct nouveau_i2c_chan *chan;
|
||||
};
|
||||
|
||||
struct parsed_dcb {
|
||||
int entries;
|
||||
struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
|
||||
struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
|
||||
};
|
||||
|
||||
enum dcb_gpio_tag {
|
||||
DCB_GPIO_TVDAC0 = 0xc,
|
||||
DCB_GPIO_TVDAC1 = 0x2d,
|
||||
};
|
||||
|
||||
struct dcb_gpio_entry {
|
||||
enum dcb_gpio_tag tag;
|
||||
int line;
|
||||
bool invert;
|
||||
};
|
||||
|
||||
struct parsed_dcb_gpio {
|
||||
int entries;
|
||||
struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
|
||||
};
|
||||
|
||||
struct dcb_connector_table_entry {
|
||||
uint32_t entry;
|
||||
uint8_t type;
|
||||
uint8_t index;
|
||||
uint8_t gpio_tag;
|
||||
};
|
||||
|
||||
struct dcb_connector_table {
|
||||
int entries;
|
||||
struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
|
||||
};
|
||||
|
||||
struct bios_parsed_dcb {
|
||||
uint8_t version;
|
||||
|
||||
struct parsed_dcb dcb;
|
||||
|
||||
uint8_t *i2c_table;
|
||||
uint8_t i2c_default_indices;
|
||||
|
||||
uint16_t gpio_table_ptr;
|
||||
struct parsed_dcb_gpio gpio;
|
||||
uint16_t connector_table_ptr;
|
||||
struct dcb_connector_table connector;
|
||||
};
|
||||
|
||||
enum nouveau_encoder_type {
|
||||
OUTPUT_ANALOG = 0,
|
||||
OUTPUT_TV = 1,
|
||||
OUTPUT_TMDS = 2,
|
||||
OUTPUT_LVDS = 3,
|
||||
OUTPUT_DP = 6,
|
||||
OUTPUT_ANY = -1
|
||||
};
|
||||
|
||||
enum nouveau_or {
|
||||
OUTPUT_A = (1 << 0),
|
||||
OUTPUT_B = (1 << 1),
|
||||
OUTPUT_C = (1 << 2)
|
||||
};
|
||||
|
||||
enum LVDS_script {
|
||||
/* Order *does* matter here */
|
||||
LVDS_INIT = 1,
|
||||
LVDS_RESET,
|
||||
LVDS_BACKLIGHT_ON,
|
||||
LVDS_BACKLIGHT_OFF,
|
||||
LVDS_PANEL_ON,
|
||||
LVDS_PANEL_OFF
|
||||
};
|
||||
|
||||
/* changing these requires matching changes to reg tables in nv_get_clock */
|
||||
#define MAX_PLL_TYPES 4
|
||||
enum pll_types {
|
||||
NVPLL,
|
||||
MPLL,
|
||||
VPLL1,
|
||||
VPLL2
|
||||
};
|
||||
|
||||
struct pll_lims {
|
||||
struct {
|
||||
int minfreq;
|
||||
int maxfreq;
|
||||
int min_inputfreq;
|
||||
int max_inputfreq;
|
||||
|
||||
uint8_t min_m;
|
||||
uint8_t max_m;
|
||||
uint8_t min_n;
|
||||
uint8_t max_n;
|
||||
} vco1, vco2;
|
||||
|
||||
uint8_t max_log2p;
|
||||
/*
|
||||
* for most pre nv50 cards setting a log2P of 7 (the common max_log2p
|
||||
* value) is no different to 6 (at least for vplls) so allowing the MNP
|
||||
* calc to use 7 causes the generated clock to be out by a factor of 2.
|
||||
* however, max_log2p cannot be fixed-up during parsing as the
|
||||
* unmodified max_log2p value is still needed for setting mplls, hence
|
||||
* an additional max_usable_log2p member
|
||||
*/
|
||||
uint8_t max_usable_log2p;
|
||||
uint8_t log2p_bias;
|
||||
|
||||
uint8_t min_p;
|
||||
uint8_t max_p;
|
||||
|
||||
int refclk;
|
||||
};
|
||||
|
||||
struct nouveau_bios_info {
|
||||
struct parsed_dcb *dcb;
|
||||
|
||||
uint8_t chip_version;
|
||||
|
||||
uint32_t dactestval;
|
||||
uint32_t tvdactestval;
|
||||
uint8_t digital_min_front_porch;
|
||||
bool fp_no_ddc;
|
||||
};
|
||||
|
||||
struct nvbios {
|
||||
struct drm_device *dev;
|
||||
struct nouveau_bios_info pub;
|
||||
|
||||
uint8_t data[NV_PROM_SIZE];
|
||||
unsigned int length;
|
||||
bool execute;
|
||||
|
||||
uint8_t major_version;
|
||||
uint8_t feature_byte;
|
||||
bool is_mobile;
|
||||
|
||||
uint32_t fmaxvco, fminvco;
|
||||
|
||||
bool old_style_init;
|
||||
uint16_t init_script_tbls_ptr;
|
||||
uint16_t extra_init_script_tbl_ptr;
|
||||
uint16_t macro_index_tbl_ptr;
|
||||
uint16_t macro_tbl_ptr;
|
||||
uint16_t condition_tbl_ptr;
|
||||
uint16_t io_condition_tbl_ptr;
|
||||
uint16_t io_flag_condition_tbl_ptr;
|
||||
uint16_t init_function_tbl_ptr;
|
||||
|
||||
uint16_t pll_limit_tbl_ptr;
|
||||
uint16_t ram_restrict_tbl_ptr;
|
||||
|
||||
uint16_t some_script_ptr; /* BIT I + 14 */
|
||||
uint16_t init96_tbl_ptr; /* BIT I + 16 */
|
||||
|
||||
struct bios_parsed_dcb bdcb;
|
||||
|
||||
struct {
|
||||
int crtchead;
|
||||
/* these need remembering across suspend */
|
||||
uint32_t saved_nv_pfb_cfg0;
|
||||
} state;
|
||||
|
||||
struct {
|
||||
struct dcb_entry *output;
|
||||
uint16_t script_table_ptr;
|
||||
uint16_t dp_table_ptr;
|
||||
} display;
|
||||
|
||||
struct {
|
||||
uint16_t fptablepointer; /* also used by tmds */
|
||||
uint16_t fpxlatetableptr;
|
||||
int xlatwidth;
|
||||
uint16_t lvdsmanufacturerpointer;
|
||||
uint16_t fpxlatemanufacturertableptr;
|
||||
uint16_t mode_ptr;
|
||||
uint16_t xlated_entry;
|
||||
bool power_off_for_reset;
|
||||
bool reset_after_pclk_change;
|
||||
bool dual_link;
|
||||
bool link_c_increment;
|
||||
bool BITbit1;
|
||||
bool if_is_24bit;
|
||||
int duallink_transition_clk;
|
||||
uint8_t strapless_is_24bit;
|
||||
uint8_t *edid;
|
||||
|
||||
/* will need resetting after suspend */
|
||||
int last_script_invoc;
|
||||
bool lvds_init_run;
|
||||
} fp;
|
||||
|
||||
struct {
|
||||
uint16_t output0_script_ptr;
|
||||
uint16_t output1_script_ptr;
|
||||
} tmds;
|
||||
|
||||
struct {
|
||||
uint16_t mem_init_tbl_ptr;
|
||||
uint16_t sdr_seq_tbl_ptr;
|
||||
uint16_t ddr_seq_tbl_ptr;
|
||||
|
||||
struct {
|
||||
uint8_t crt, tv, panel;
|
||||
} i2c_indices;
|
||||
|
||||
uint16_t lvds_single_a_script_ptr;
|
||||
} legacy;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
* Copyright 1993-2003 NVIDIA, Corporation
|
||||
* Copyright 2007-2009 Stuart Bennett
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_hw.h"
|
||||
|
||||
/****************************************************************************\
|
||||
* *
|
||||
* The video arbitration routines calculate some "magic" numbers. Fixes *
|
||||
* the snow seen when accessing the framebuffer without it. *
|
||||
* It just works (I hope). *
|
||||
* *
|
||||
\****************************************************************************/
|
||||
|
||||
struct nv_fifo_info {
|
||||
int lwm;
|
||||
int burst;
|
||||
};
|
||||
|
||||
struct nv_sim_state {
|
||||
int pclk_khz;
|
||||
int mclk_khz;
|
||||
int nvclk_khz;
|
||||
int bpp;
|
||||
int mem_page_miss;
|
||||
int mem_latency;
|
||||
int memory_type;
|
||||
int memory_width;
|
||||
int two_heads;
|
||||
};
|
||||
|
||||
static void
|
||||
nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
|
||||
{
|
||||
int pagemiss, cas, width, bpp;
|
||||
int nvclks, mclks, pclks, crtpagemiss;
|
||||
int found, mclk_extra, mclk_loop, cbs, m1, p1;
|
||||
int mclk_freq, pclk_freq, nvclk_freq;
|
||||
int us_m, us_n, us_p, crtc_drain_rate;
|
||||
int cpm_us, us_crt, clwm;
|
||||
|
||||
pclk_freq = arb->pclk_khz;
|
||||
mclk_freq = arb->mclk_khz;
|
||||
nvclk_freq = arb->nvclk_khz;
|
||||
pagemiss = arb->mem_page_miss;
|
||||
cas = arb->mem_latency;
|
||||
width = arb->memory_width >> 6;
|
||||
bpp = arb->bpp;
|
||||
cbs = 128;
|
||||
|
||||
pclks = 2;
|
||||
nvclks = 10;
|
||||
mclks = 13 + cas;
|
||||
mclk_extra = 3;
|
||||
found = 0;
|
||||
|
||||
while (!found) {
|
||||
found = 1;
|
||||
|
||||
mclk_loop = mclks + mclk_extra;
|
||||
us_m = mclk_loop * 1000 * 1000 / mclk_freq;
|
||||
us_n = nvclks * 1000 * 1000 / nvclk_freq;
|
||||
us_p = nvclks * 1000 * 1000 / pclk_freq;
|
||||
|
||||
crtc_drain_rate = pclk_freq * bpp / 8;
|
||||
crtpagemiss = 2;
|
||||
crtpagemiss += 1;
|
||||
cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
|
||||
us_crt = cpm_us + us_m + us_n + us_p;
|
||||
clwm = us_crt * crtc_drain_rate / (1000 * 1000);
|
||||
clwm++;
|
||||
|
||||
m1 = clwm + cbs - 512;
|
||||
p1 = m1 * pclk_freq / mclk_freq;
|
||||
p1 = p1 * bpp / 8;
|
||||
if ((p1 < m1 && m1 > 0) || clwm > 519) {
|
||||
found = !mclk_extra;
|
||||
mclk_extra--;
|
||||
}
|
||||
if (clwm < 384)
|
||||
clwm = 384;
|
||||
|
||||
fifo->lwm = clwm;
|
||||
fifo->burst = cbs;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
|
||||
{
|
||||
int fill_rate, drain_rate;
|
||||
int pclks, nvclks, mclks, xclks;
|
||||
int pclk_freq, nvclk_freq, mclk_freq;
|
||||
int fill_lat, extra_lat;
|
||||
int max_burst_o, max_burst_l;
|
||||
int fifo_len, min_lwm, max_lwm;
|
||||
const int burst_lat = 80; /* Maximum allowable latency due
|
||||
* to the CRTC FIFO burst. (ns) */
|
||||
|
||||
pclk_freq = arb->pclk_khz;
|
||||
nvclk_freq = arb->nvclk_khz;
|
||||
mclk_freq = arb->mclk_khz;
|
||||
|
||||
fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */
|
||||
drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */
|
||||
|
||||
fifo_len = arb->two_heads ? 1536 : 1024; /* B */
|
||||
|
||||
/* Fixed FIFO refill latency. */
|
||||
|
||||
pclks = 4; /* lwm detect. */
|
||||
|
||||
nvclks = 3 /* lwm -> sync. */
|
||||
+ 2 /* fbi bus cycles (1 req + 1 busy) */
|
||||
+ 1 /* 2 edge sync. may be very close to edge so
|
||||
* just put one. */
|
||||
+ 1 /* fbi_d_rdv_n */
|
||||
+ 1 /* Fbi_d_rdata */
|
||||
+ 1; /* crtfifo load */
|
||||
|
||||
mclks = 1 /* 2 edge sync. may be very close to edge so
|
||||
* just put one. */
|
||||
+ 1 /* arb_hp_req */
|
||||
+ 5 /* tiling pipeline */
|
||||
+ 2 /* latency fifo */
|
||||
+ 2 /* memory request to fbio block */
|
||||
+ 7; /* data returned from fbio block */
|
||||
|
||||
/* Need to accumulate 256 bits for read */
|
||||
mclks += (arb->memory_type == 0 ? 2 : 1)
|
||||
* arb->memory_width / 32;
|
||||
|
||||
fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */
|
||||
+ nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */
|
||||
+ pclks * 1000 * 1000 / pclk_freq; /* pclk latency */
|
||||
|
||||
/* Conditional FIFO refill latency. */
|
||||
|
||||
xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to
|
||||
* the overlay. */
|
||||
+ 2 * arb->mem_page_miss /* Extra pagemiss latency. */
|
||||
+ (arb->bpp == 32 ? 8 : 4); /* Margin of error. */
|
||||
|
||||
extra_lat = xclks * 1000 * 1000 / mclk_freq;
|
||||
|
||||
if (arb->two_heads)
|
||||
/* Account for another CRTC. */
|
||||
extra_lat += fill_lat + extra_lat + burst_lat;
|
||||
|
||||
/* FIFO burst */
|
||||
|
||||
/* Max burst not leading to overflows. */
|
||||
max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000))
|
||||
* (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000);
|
||||
fifo->burst = min(max_burst_o, 1024);
|
||||
|
||||
/* Max burst value with an acceptable latency. */
|
||||
max_burst_l = burst_lat * fill_rate / (1000 * 1000);
|
||||
fifo->burst = min(max_burst_l, fifo->burst);
|
||||
|
||||
fifo->burst = rounddown_pow_of_two(fifo->burst);
|
||||
|
||||
/* FIFO low watermark */
|
||||
|
||||
min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1;
|
||||
max_lwm = fifo_len - fifo->burst
|
||||
+ fill_lat * drain_rate / (1000 * 1000)
|
||||
+ fifo->burst * drain_rate / fill_rate;
|
||||
|
||||
fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
|
||||
int *burst, int *lwm)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nv_fifo_info fifo_data;
|
||||
struct nv_sim_state sim_data;
|
||||
int MClk = nouveau_hw_get_clock(dev, MPLL);
|
||||
int NVClk = nouveau_hw_get_clock(dev, NVPLL);
|
||||
uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1);
|
||||
|
||||
sim_data.pclk_khz = VClk;
|
||||
sim_data.mclk_khz = MClk;
|
||||
sim_data.nvclk_khz = NVClk;
|
||||
sim_data.bpp = bpp;
|
||||
sim_data.two_heads = nv_two_heads(dev);
|
||||
if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
|
||||
(dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
|
||||
uint32_t type;
|
||||
|
||||
pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type);
|
||||
|
||||
sim_data.memory_type = (type >> 12) & 1;
|
||||
sim_data.memory_width = 64;
|
||||
sim_data.mem_latency = 3;
|
||||
sim_data.mem_page_miss = 10;
|
||||
} else {
|
||||
sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1;
|
||||
sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
|
||||
sim_data.mem_latency = cfg1 & 0xf;
|
||||
sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
|
||||
}
|
||||
|
||||
if (dev_priv->card_type == NV_04)
|
||||
nv04_calc_arb(&fifo_data, &sim_data);
|
||||
else
|
||||
nv10_calc_arb(&fifo_data, &sim_data);
|
||||
|
||||
*burst = ilog2(fifo_data.burst >> 4);
|
||||
*lwm = fifo_data.lwm >> 3;
|
||||
}
|
||||
|
||||
static void
|
||||
nv30_update_arb(int *burst, int *lwm)
|
||||
{
|
||||
unsigned int fifo_size, burst_size, graphics_lwm;
|
||||
|
||||
fifo_size = 2048;
|
||||
burst_size = 512;
|
||||
graphics_lwm = fifo_size - burst_size;
|
||||
|
||||
*burst = ilog2(burst_size >> 5);
|
||||
*lwm = graphics_lwm >> 3;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (dev_priv->card_type < NV_30)
|
||||
nv04_update_arb(dev, vclk, bpp, burst, lwm);
|
||||
else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
|
||||
(dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
|
||||
*burst = 128;
|
||||
*lwm = 0x0480;
|
||||
} else
|
||||
nv30_update_arb(burst, lwm);
|
||||
}
|
||||
|
||||
static int
|
||||
getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
|
||||
struct nouveau_pll_vals *bestpv)
|
||||
{
|
||||
/* Find M, N and P for a single stage PLL
|
||||
*
|
||||
* Note that some bioses (NV3x) have lookup tables of precomputed MNP
|
||||
* values, but we're too lazy to use those atm
|
||||
*
|
||||
* "clk" parameter in kHz
|
||||
* returns calculated clock
|
||||
*/
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
int cv = dev_priv->vbios->chip_version;
|
||||
int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
|
||||
int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
|
||||
int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
|
||||
int minU = pll_lim->vco1.min_inputfreq;
|
||||
int maxU = pll_lim->vco1.max_inputfreq;
|
||||
int minP = pll_lim->max_p ? pll_lim->min_p : 0;
|
||||
int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p;
|
||||
int crystal = pll_lim->refclk;
|
||||
int M, N, thisP, P;
|
||||
int clkP, calcclk;
|
||||
int delta, bestdelta = INT_MAX;
|
||||
int bestclk = 0;
|
||||
|
||||
/* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
|
||||
/* possibly correlated with introduction of 27MHz crystal */
|
||||
if (dev_priv->card_type < NV_50) {
|
||||
if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
|
||||
if (clk > 250000)
|
||||
maxM = 6;
|
||||
if (clk > 340000)
|
||||
maxM = 2;
|
||||
} else if (cv < 0x40) {
|
||||
if (clk > 150000)
|
||||
maxM = 6;
|
||||
if (clk > 200000)
|
||||
maxM = 4;
|
||||
if (clk > 340000)
|
||||
maxM = 2;
|
||||
}
|
||||
}
|
||||
|
||||
P = pll_lim->max_p ? maxP : (1 << maxP);
|
||||
if ((clk * P) < minvco) {
|
||||
minvco = clk * maxP;
|
||||
maxvco = minvco * 2;
|
||||
}
|
||||
|
||||
if (clk + clk/200 > maxvco) /* +0.5% */
|
||||
maxvco = clk + clk/200;
|
||||
|
||||
/* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
|
||||
for (thisP = minP; thisP <= maxP; thisP++) {
|
||||
P = pll_lim->max_p ? thisP : (1 << thisP);
|
||||
clkP = clk * P;
|
||||
|
||||
if (clkP < minvco)
|
||||
continue;
|
||||
if (clkP > maxvco)
|
||||
return bestclk;
|
||||
|
||||
for (M = minM; M <= maxM; M++) {
|
||||
if (crystal/M < minU)
|
||||
return bestclk;
|
||||
if (crystal/M > maxU)
|
||||
continue;
|
||||
|
||||
/* add crystal/2 to round better */
|
||||
N = (clkP * M + crystal/2) / crystal;
|
||||
|
||||
if (N < minN)
|
||||
continue;
|
||||
if (N > maxN)
|
||||
break;
|
||||
|
||||
/* more rounding additions */
|
||||
calcclk = ((N * crystal + P/2) / P + M/2) / M;
|
||||
delta = abs(calcclk - clk);
|
||||
/* we do an exhaustive search rather than terminating
|
||||
* on an optimality condition...
|
||||
*/
|
||||
if (delta < bestdelta) {
|
||||
bestdelta = delta;
|
||||
bestclk = calcclk;
|
||||
bestpv->N1 = N;
|
||||
bestpv->M1 = M;
|
||||
bestpv->log2P = thisP;
|
||||
if (delta == 0) /* except this one */
|
||||
return bestclk;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestclk;
|
||||
}
|
||||
|
||||
static int
|
||||
getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
|
||||
struct nouveau_pll_vals *bestpv)
|
||||
{
|
||||
/* Find M, N and P for a two stage PLL
|
||||
*
|
||||
* Note that some bioses (NV30+) have lookup tables of precomputed MNP
|
||||
* values, but we're too lazy to use those atm
|
||||
*
|
||||
* "clk" parameter in kHz
|
||||
* returns calculated clock
|
||||
*/
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
int chip_version = dev_priv->vbios->chip_version;
|
||||
int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
|
||||
int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
|
||||
int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
|
||||
int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
|
||||
int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
|
||||
int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
|
||||
int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
|
||||
int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
|
||||
int maxlog2P = pll_lim->max_usable_log2p;
|
||||
int crystal = pll_lim->refclk;
|
||||
bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
|
||||
int M1, N1, M2, N2, log2P;
|
||||
int clkP, calcclk1, calcclk2, calcclkout;
|
||||
int delta, bestdelta = INT_MAX;
|
||||
int bestclk = 0;
|
||||
|
||||
int vco2 = (maxvco2 - maxvco2/200) / 2;
|
||||
for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
|
||||
;
|
||||
clkP = clk << log2P;
|
||||
|
||||
if (maxvco2 < clk + clk/200) /* +0.5% */
|
||||
maxvco2 = clk + clk/200;
|
||||
|
||||
for (M1 = minM1; M1 <= maxM1; M1++) {
|
||||
if (crystal/M1 < minU1)
|
||||
return bestclk;
|
||||
if (crystal/M1 > maxU1)
|
||||
continue;
|
||||
|
||||
for (N1 = minN1; N1 <= maxN1; N1++) {
|
||||
calcclk1 = crystal * N1 / M1;
|
||||
if (calcclk1 < minvco1)
|
||||
continue;
|
||||
if (calcclk1 > maxvco1)
|
||||
break;
|
||||
|
||||
for (M2 = minM2; M2 <= maxM2; M2++) {
|
||||
if (calcclk1/M2 < minU2)
|
||||
break;
|
||||
if (calcclk1/M2 > maxU2)
|
||||
continue;
|
||||
|
||||
/* add calcclk1/2 to round better */
|
||||
N2 = (clkP * M2 + calcclk1/2) / calcclk1;
|
||||
if (N2 < minN2)
|
||||
continue;
|
||||
if (N2 > maxN2)
|
||||
break;
|
||||
|
||||
if (!fixedgain2) {
|
||||
if (chip_version < 0x60)
|
||||
if (N2/M2 < 4 || N2/M2 > 10)
|
||||
continue;
|
||||
|
||||
calcclk2 = calcclk1 * N2 / M2;
|
||||
if (calcclk2 < minvco2)
|
||||
break;
|
||||
if (calcclk2 > maxvco2)
|
||||
continue;
|
||||
} else
|
||||
calcclk2 = calcclk1;
|
||||
|
||||
calcclkout = calcclk2 >> log2P;
|
||||
delta = abs(calcclkout - clk);
|
||||
/* we do an exhaustive search rather than terminating
|
||||
* on an optimality condition...
|
||||
*/
|
||||
if (delta < bestdelta) {
|
||||
bestdelta = delta;
|
||||
bestclk = calcclkout;
|
||||
bestpv->N1 = N1;
|
||||
bestpv->M1 = M1;
|
||||
bestpv->N2 = N2;
|
||||
bestpv->M2 = M2;
|
||||
bestpv->log2P = log2P;
|
||||
if (delta == 0) /* except this one */
|
||||
return bestclk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestclk;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
|
||||
struct nouveau_pll_vals *pv)
|
||||
{
|
||||
int outclk;
|
||||
|
||||
if (!pll_lim->vco2.maxfreq)
|
||||
outclk = getMNP_single(dev, pll_lim, clk, pv);
|
||||
else
|
||||
outclk = getMNP_double(dev, pll_lim, clk, pv);
|
||||
|
||||
if (!outclk)
|
||||
NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
|
||||
|
||||
return outclk;
|
||||
}
|
||||
@@ -0,0 +1,468 @@
|
||||
/*
|
||||
* Copyright 2005-2006 Stephane Marchesin
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_dma.h"
|
||||
|
||||
static int
|
||||
nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_bo *pb = chan->pushbuf_bo;
|
||||
struct nouveau_gpuobj *pushbuf = NULL;
|
||||
uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT;
|
||||
int ret;
|
||||
|
||||
if (pb->bo.mem.mem_type == TTM_PL_TT) {
|
||||
ret = nouveau_gpuobj_gart_dma_new(chan, 0,
|
||||
dev_priv->gart_info.aper_size,
|
||||
NV_DMA_ACCESS_RO, &pushbuf,
|
||||
NULL);
|
||||
chan->pushbuf_base = start;
|
||||
} else
|
||||
if (dev_priv->card_type != NV_04) {
|
||||
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
|
||||
dev_priv->fb_available_size,
|
||||
NV_DMA_ACCESS_RO,
|
||||
NV_DMA_TARGET_VIDMEM, &pushbuf);
|
||||
chan->pushbuf_base = start;
|
||||
} else {
|
||||
/* NV04 cmdbuf hack, from original ddx.. not sure of it's
|
||||
* exact reason for existing :) PCI access to cmdbuf in
|
||||
* VRAM.
|
||||
*/
|
||||
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
|
||||
drm_get_resource_start(dev, 1),
|
||||
dev_priv->fb_available_size,
|
||||
NV_DMA_ACCESS_RO,
|
||||
NV_DMA_TARGET_PCI, &pushbuf);
|
||||
chan->pushbuf_base = start;
|
||||
}
|
||||
|
||||
ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret);
|
||||
if (pushbuf != dev_priv->gart_info.sg_ctxdma)
|
||||
nouveau_gpuobj_del(dev, &pushbuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nouveau_bo *
|
||||
nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_bo *pushbuf = NULL;
|
||||
int location, ret;
|
||||
|
||||
if (nouveau_vram_pushbuf)
|
||||
location = TTM_PL_FLAG_VRAM;
|
||||
else
|
||||
location = TTM_PL_FLAG_TT;
|
||||
|
||||
ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false,
|
||||
true, &pushbuf);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = nouveau_bo_pin(pushbuf, location);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
|
||||
nouveau_bo_ref(NULL, &pushbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pushbuf;
|
||||
}
|
||||
|
||||
/* allocates and initializes a fifo for user space consumption */
|
||||
int
|
||||
nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
|
||||
struct drm_file *file_priv,
|
||||
uint32_t vram_handle, uint32_t tt_handle)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
||||
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
||||
struct nouveau_channel *chan;
|
||||
int channel, user;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Alright, here is the full story
|
||||
* Nvidia cards have multiple hw fifo contexts (praise them for that,
|
||||
* no complicated crash-prone context switches)
|
||||
* We allocate a new context for each app and let it write to it
|
||||
* directly (woo, full userspace command submission !)
|
||||
* When there are no more contexts, you lost
|
||||
*/
|
||||
for (channel = 0; channel < pfifo->channels; channel++) {
|
||||
if (dev_priv->fifos[channel] == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
/* no more fifos. you lost. */
|
||||
if (channel == pfifo->channels)
|
||||
return -EINVAL;
|
||||
|
||||
dev_priv->fifos[channel] = kzalloc(sizeof(struct nouveau_channel),
|
||||
GFP_KERNEL);
|
||||
if (!dev_priv->fifos[channel])
|
||||
return -ENOMEM;
|
||||
dev_priv->fifo_alloc_count++;
|
||||
chan = dev_priv->fifos[channel];
|
||||
INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
|
||||
INIT_LIST_HEAD(&chan->fence.pending);
|
||||
chan->dev = dev;
|
||||
chan->id = channel;
|
||||
chan->file_priv = file_priv;
|
||||
chan->vram_handle = vram_handle;
|
||||
chan->gart_handle = tt_handle;
|
||||
|
||||
NV_INFO(dev, "Allocating FIFO number %d\n", channel);
|
||||
|
||||
/* Allocate DMA push buffer */
|
||||
chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
|
||||
if (!chan->pushbuf_bo) {
|
||||
ret = -ENOMEM;
|
||||
NV_ERROR(dev, "pushbuf %d\n", ret);
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Locate channel's user control regs */
|
||||
if (dev_priv->card_type < NV_40)
|
||||
user = NV03_USER(channel);
|
||||
else
|
||||
if (dev_priv->card_type < NV_50)
|
||||
user = NV40_USER(channel);
|
||||
else
|
||||
user = NV50_USER(channel);
|
||||
|
||||
chan->user = ioremap(pci_resource_start(dev->pdev, 0) + user,
|
||||
PAGE_SIZE);
|
||||
if (!chan->user) {
|
||||
NV_ERROR(dev, "ioremap of regs failed.\n");
|
||||
nouveau_channel_free(chan);
|
||||
return -ENOMEM;
|
||||
}
|
||||
chan->user_put = 0x40;
|
||||
chan->user_get = 0x44;
|
||||
|
||||
/* Allocate space for per-channel fixed notifier memory */
|
||||
ret = nouveau_notifier_init_channel(chan);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "ntfy %d\n", ret);
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Setup channel's default objects */
|
||||
ret = nouveau_gpuobj_channel_init(chan, vram_handle, tt_handle);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "gpuobj %d\n", ret);
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create a dma object for the push buffer */
|
||||
ret = nouveau_channel_pushbuf_ctxdma_init(chan);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "pbctxdma %d\n", ret);
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* disable the fifo caches */
|
||||
pfifo->reassign(dev, false);
|
||||
|
||||
/* Create a graphics context for new channel */
|
||||
ret = pgraph->create_context(chan);
|
||||
if (ret) {
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Construct inital RAMFC for new channel */
|
||||
ret = pfifo->create_context(chan);
|
||||
if (ret) {
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pfifo->reassign(dev, true);
|
||||
|
||||
ret = nouveau_dma_init(chan);
|
||||
if (!ret)
|
||||
ret = nouveau_fence_init(chan);
|
||||
if (ret) {
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nouveau_debugfs_channel_init(chan);
|
||||
|
||||
NV_INFO(dev, "%s: initialised FIFO %d\n", __func__, channel);
|
||||
*chan_ret = chan;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_channel_idle(struct nouveau_channel *chan)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_engine *engine = &dev_priv->engine;
|
||||
uint32_t caches;
|
||||
int idle;
|
||||
|
||||
if (!chan) {
|
||||
NV_ERROR(dev, "no channel...\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
caches = nv_rd32(dev, NV03_PFIFO_CACHES);
|
||||
nv_wr32(dev, NV03_PFIFO_CACHES, caches & ~1);
|
||||
|
||||
if (engine->fifo.channel_id(dev) != chan->id) {
|
||||
struct nouveau_gpuobj *ramfc =
|
||||
chan->ramfc ? chan->ramfc->gpuobj : NULL;
|
||||
|
||||
if (!ramfc) {
|
||||
NV_ERROR(dev, "No RAMFC for channel %d\n", chan->id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
engine->instmem.prepare_access(dev, false);
|
||||
if (nv_ro32(dev, ramfc, 0) != nv_ro32(dev, ramfc, 1))
|
||||
idle = 0;
|
||||
else
|
||||
idle = 1;
|
||||
engine->instmem.finish_access(dev);
|
||||
} else {
|
||||
idle = (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET) ==
|
||||
nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
|
||||
}
|
||||
|
||||
nv_wr32(dev, NV03_PFIFO_CACHES, caches);
|
||||
return idle;
|
||||
}
|
||||
|
||||
/* stops a fifo */
|
||||
void
|
||||
nouveau_channel_free(struct nouveau_channel *chan)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
|
||||
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id);
|
||||
|
||||
nouveau_debugfs_channel_fini(chan);
|
||||
|
||||
/* Give outstanding push buffers a chance to complete */
|
||||
spin_lock_irqsave(&chan->fence.lock, flags);
|
||||
nouveau_fence_update(chan);
|
||||
spin_unlock_irqrestore(&chan->fence.lock, flags);
|
||||
if (chan->fence.sequence != chan->fence.sequence_ack) {
|
||||
struct nouveau_fence *fence = NULL;
|
||||
|
||||
ret = nouveau_fence_new(chan, &fence, true);
|
||||
if (ret == 0) {
|
||||
ret = nouveau_fence_wait(fence, NULL, false, false);
|
||||
nouveau_fence_unref((void *)&fence);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
|
||||
}
|
||||
|
||||
/* Ensure all outstanding fences are signaled. They should be if the
|
||||
* above attempts at idling were OK, but if we failed this'll tell TTM
|
||||
* we're done with the buffers.
|
||||
*/
|
||||
nouveau_fence_fini(chan);
|
||||
|
||||
/* Ensure the channel is no longer active on the GPU */
|
||||
pfifo->reassign(dev, false);
|
||||
|
||||
if (pgraph->channel(dev) == chan) {
|
||||
pgraph->fifo_access(dev, false);
|
||||
pgraph->unload_context(dev);
|
||||
pgraph->fifo_access(dev, true);
|
||||
}
|
||||
pgraph->destroy_context(chan);
|
||||
|
||||
if (pfifo->channel_id(dev) == chan->id) {
|
||||
pfifo->disable(dev);
|
||||
pfifo->unload_context(dev);
|
||||
pfifo->enable(dev);
|
||||
}
|
||||
pfifo->destroy_context(chan);
|
||||
|
||||
pfifo->reassign(dev, true);
|
||||
|
||||
/* Release the channel's resources */
|
||||
nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
|
||||
if (chan->pushbuf_bo) {
|
||||
nouveau_bo_unpin(chan->pushbuf_bo);
|
||||
nouveau_bo_ref(NULL, &chan->pushbuf_bo);
|
||||
}
|
||||
nouveau_gpuobj_channel_takedown(chan);
|
||||
nouveau_notifier_takedown_channel(chan);
|
||||
if (chan->user)
|
||||
iounmap(chan->user);
|
||||
|
||||
dev_priv->fifos[chan->id] = NULL;
|
||||
dev_priv->fifo_alloc_count--;
|
||||
kfree(chan);
|
||||
}
|
||||
|
||||
/* cleans up all the fifos from file_priv */
|
||||
void
|
||||
nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_engine *engine = &dev_priv->engine;
|
||||
int i;
|
||||
|
||||
NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
|
||||
for (i = 0; i < engine->fifo.channels; i++) {
|
||||
struct nouveau_channel *chan = dev_priv->fifos[i];
|
||||
|
||||
if (chan && chan->file_priv == file_priv)
|
||||
nouveau_channel_free(chan);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_channel_owner(struct drm_device *dev, struct drm_file *file_priv,
|
||||
int channel)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_engine *engine = &dev_priv->engine;
|
||||
|
||||
if (channel >= engine->fifo.channels)
|
||||
return 0;
|
||||
if (dev_priv->fifos[channel] == NULL)
|
||||
return 0;
|
||||
|
||||
return (dev_priv->fifos[channel]->file_priv == file_priv);
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* ioctls wrapping the functions
|
||||
***********************************/
|
||||
|
||||
static int
|
||||
nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct drm_nouveau_channel_alloc *init = data;
|
||||
struct nouveau_channel *chan;
|
||||
int ret;
|
||||
|
||||
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
||||
|
||||
if (dev_priv->engine.graph.accel_blocked)
|
||||
return -ENODEV;
|
||||
|
||||
if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = nouveau_channel_alloc(dev, &chan, file_priv,
|
||||
init->fb_ctxdma_handle,
|
||||
init->tt_ctxdma_handle);
|
||||
if (ret)
|
||||
return ret;
|
||||
init->channel = chan->id;
|
||||
|
||||
init->subchan[0].handle = NvM2MF;
|
||||
if (dev_priv->card_type < NV_50)
|
||||
init->subchan[0].grclass = 0x0039;
|
||||
else
|
||||
init->subchan[0].grclass = 0x5039;
|
||||
init->nr_subchan = 1;
|
||||
|
||||
/* Named memory object area */
|
||||
ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
|
||||
&init->notifier_handle);
|
||||
if (ret) {
|
||||
nouveau_channel_free(chan);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_nouveau_channel_free *cfree = data;
|
||||
struct nouveau_channel *chan;
|
||||
|
||||
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
|
||||
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan);
|
||||
|
||||
nouveau_channel_free(chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***********************************
|
||||
* finally, the ioctl table
|
||||
***********************************/
|
||||
|
||||
struct drm_ioctl_desc nouveau_ioctls[] = {
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_FREE, nouveau_ioctl_fifo_free, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GROBJ_ALLOC, nouveau_ioctl_grobj_alloc, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_ioctl_notifier_alloc, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH),
|
||||
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH),
|
||||
};
|
||||
|
||||
int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NOUVEAU_CONNECTOR_H__
|
||||
#define __NOUVEAU_CONNECTOR_H__
|
||||
|
||||
#include "drm_edid.h"
|
||||
#include "nouveau_i2c.h"
|
||||
|
||||
struct nouveau_connector {
|
||||
struct drm_connector base;
|
||||
|
||||
struct dcb_connector_table_entry *dcb;
|
||||
|
||||
int scaling_mode;
|
||||
bool use_dithering;
|
||||
|
||||
struct nouveau_encoder *detected_encoder;
|
||||
struct edid *edid;
|
||||
struct drm_display_mode *native_mode;
|
||||
};
|
||||
|
||||
static inline struct nouveau_connector *nouveau_connector(
|
||||
struct drm_connector *con)
|
||||
{
|
||||
return container_of(con, struct nouveau_connector, base);
|
||||
}
|
||||
|
||||
int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
|
||||
|
||||
#endif /* __NOUVEAU_CONNECTOR_H__ */
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __NOUVEAU_CRTC_H__
|
||||
#define __NOUVEAU_CRTC_H__
|
||||
|
||||
struct nouveau_crtc {
|
||||
struct drm_crtc base;
|
||||
|
||||
int index;
|
||||
|
||||
struct drm_display_mode *mode;
|
||||
|
||||
uint32_t dpms_saved_fp_control;
|
||||
uint32_t fp_users;
|
||||
int saturation;
|
||||
int sharpness;
|
||||
int last_dpms;
|
||||
|
||||
struct {
|
||||
int cpp;
|
||||
bool blanked;
|
||||
uint32_t offset;
|
||||
uint32_t tile_flags;
|
||||
} fb;
|
||||
|
||||
struct {
|
||||
struct nouveau_bo *nvbo;
|
||||
bool visible;
|
||||
uint32_t offset;
|
||||
void (*set_offset)(struct nouveau_crtc *, uint32_t offset);
|
||||
void (*set_pos)(struct nouveau_crtc *, int x, int y);
|
||||
void (*hide)(struct nouveau_crtc *, bool update);
|
||||
void (*show)(struct nouveau_crtc *, bool update);
|
||||
} cursor;
|
||||
|
||||
struct {
|
||||
struct nouveau_bo *nvbo;
|
||||
uint16_t r[256];
|
||||
uint16_t g[256];
|
||||
uint16_t b[256];
|
||||
int depth;
|
||||
} lut;
|
||||
|
||||
int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
|
||||
int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
|
||||
};
|
||||
|
||||
static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
|
||||
{
|
||||
return container_of(crtc, struct nouveau_crtc, base);
|
||||
}
|
||||
|
||||
static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
|
||||
{
|
||||
return &crtc->base;
|
||||
}
|
||||
|
||||
int nv50_crtc_create(struct drm_device *dev, int index);
|
||||
int nv50_cursor_init(struct nouveau_crtc *);
|
||||
void nv50_cursor_fini(struct nouveau_crtc *);
|
||||
int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
|
||||
uint32_t buffer_handle, uint32_t width,
|
||||
uint32_t height);
|
||||
int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
|
||||
|
||||
int nv04_cursor_init(struct nouveau_crtc *);
|
||||
|
||||
struct nouveau_connector *
|
||||
nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
|
||||
|
||||
#endif /* __NOUVEAU_CRTC_H__ */
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2009 Red Hat <bskeggs@redhat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Authors:
|
||||
* Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include "drmP.h"
|
||||
#include "nouveau_drv.h"
|
||||
|
||||
static int
|
||||
nouveau_debugfs_channel_info(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct nouveau_channel *chan = node->info_ent->data;
|
||||
|
||||
seq_printf(m, "channel id : %d\n", chan->id);
|
||||
|
||||
seq_printf(m, "cpu fifo state:\n");
|
||||
seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base);
|
||||
seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2);
|
||||
seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2);
|
||||
seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2);
|
||||
seq_printf(m, " free: 0x%08x\n", chan->dma.free << 2);
|
||||
|
||||
seq_printf(m, "gpu fifo state:\n");
|
||||
seq_printf(m, " get: 0x%08x\n",
|
||||
nvchan_rd32(chan, chan->user_get));
|
||||
seq_printf(m, " put: 0x%08x\n",
|
||||
nvchan_rd32(chan, chan->user_put));
|
||||
|
||||
seq_printf(m, "last fence : %d\n", chan->fence.sequence);
|
||||
seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_debugfs_channel_init(struct nouveau_channel *chan)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
||||
struct drm_minor *minor = chan->dev->primary;
|
||||
int ret;
|
||||
|
||||
if (!dev_priv->debugfs.channel_root) {
|
||||
dev_priv->debugfs.channel_root =
|
||||
debugfs_create_dir("channel", minor->debugfs_root);
|
||||
if (!dev_priv->debugfs.channel_root)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
snprintf(chan->debugfs.name, 32, "%d", chan->id);
|
||||
chan->debugfs.info.name = chan->debugfs.name;
|
||||
chan->debugfs.info.show = nouveau_debugfs_channel_info;
|
||||
chan->debugfs.info.driver_features = 0;
|
||||
chan->debugfs.info.data = chan;
|
||||
|
||||
ret = drm_debugfs_create_files(&chan->debugfs.info, 1,
|
||||
dev_priv->debugfs.channel_root,
|
||||
chan->dev->primary);
|
||||
if (ret == 0)
|
||||
chan->debugfs.active = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
||||
|
||||
if (!chan->debugfs.active)
|
||||
return;
|
||||
|
||||
drm_debugfs_remove_files(&chan->debugfs.info, 1, chan->dev->primary);
|
||||
chan->debugfs.active = false;
|
||||
|
||||
if (chan == dev_priv->channel) {
|
||||
debugfs_remove(dev_priv->debugfs.channel_root);
|
||||
dev_priv->debugfs.channel_root = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_debugfs_chipset_info(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_minor *minor = node->minor;
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
uint32_t ppci_0;
|
||||
|
||||
ppci_0 = nv_rd32(dev, dev_priv->chipset >= 0x40 ? 0x88000 : 0x1800);
|
||||
|
||||
seq_printf(m, "PMC_BOOT_0: 0x%08x\n", nv_rd32(dev, NV03_PMC_BOOT_0));
|
||||
seq_printf(m, "PCI ID : 0x%04x:0x%04x\n",
|
||||
ppci_0 & 0xffff, ppci_0 >> 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_debugfs_memory_info(struct seq_file *m, void *data)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_minor *minor = node->minor;
|
||||
struct drm_device *dev = minor->dev;
|
||||
|
||||
seq_printf(m, "VRAM total: %dKiB\n",
|
||||
(int)(nouveau_mem_fb_amount(dev) >> 10));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_info_list nouveau_debugfs_list[] = {
|
||||
{ "chipset", nouveau_debugfs_chipset_info, 0, NULL },
|
||||
{ "memory", nouveau_debugfs_memory_info, 0, NULL },
|
||||
};
|
||||
#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
|
||||
|
||||
int
|
||||
nouveau_debugfs_init(struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
|
||||
minor->debugfs_root, minor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_debugfs_takedown(struct drm_minor *minor)
|
||||
{
|
||||
drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
|
||||
minor);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2008 Maarten Maathuis.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_fb.h"
|
||||
#include "nouveau_fbcon.h"
|
||||
|
||||
static void
|
||||
nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
|
||||
{
|
||||
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
||||
struct drm_device *dev = drm_fb->dev;
|
||||
|
||||
if (drm_fb->fbdev)
|
||||
nouveau_fbcon_remove(dev, drm_fb);
|
||||
|
||||
if (fb->nvbo) {
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
drm_gem_object_unreference(fb->nvbo->gem);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
drm_framebuffer_cleanup(drm_fb);
|
||||
kfree(fb);
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle)
|
||||
{
|
||||
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
||||
|
||||
return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);
|
||||
}
|
||||
|
||||
static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
|
||||
.destroy = nouveau_user_framebuffer_destroy,
|
||||
.create_handle = nouveau_user_framebuffer_create_handle,
|
||||
};
|
||||
|
||||
struct drm_framebuffer *
|
||||
nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo,
|
||||
struct drm_mode_fb_cmd *mode_cmd)
|
||||
{
|
||||
struct nouveau_framebuffer *fb;
|
||||
int ret;
|
||||
|
||||
fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
|
||||
if (!fb)
|
||||
return NULL;
|
||||
|
||||
ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs);
|
||||
if (ret) {
|
||||
kfree(fb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
|
||||
|
||||
fb->nvbo = nvbo;
|
||||
return &fb->base;
|
||||
}
|
||||
|
||||
static struct drm_framebuffer *
|
||||
nouveau_user_framebuffer_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
struct drm_mode_fb_cmd *mode_cmd)
|
||||
{
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_gem_object *gem;
|
||||
|
||||
gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
|
||||
if (!gem)
|
||||
return NULL;
|
||||
|
||||
fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd);
|
||||
if (!fb) {
|
||||
drm_gem_object_unreference(gem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
|
||||
.fb_create = nouveau_user_framebuffer_create,
|
||||
.fb_changed = nouveau_fbcon_probe,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2007 Ben Skeggs.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial
|
||||
* portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "drm.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_dma.h"
|
||||
|
||||
int
|
||||
nouveau_dma_init(struct nouveau_channel *chan)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_gpuobj *m2mf = NULL;
|
||||
int ret, i;
|
||||
|
||||
/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
|
||||
ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
|
||||
0x0039 : 0x5039, &m2mf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
|
||||
ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Map push buffer */
|
||||
ret = nouveau_bo_map(chan->pushbuf_bo);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Map M2MF notifier object - fbcon. */
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
ret = nouveau_bo_map(chan->notifier_bo);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Initialise DMA vars */
|
||||
chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
|
||||
chan->dma.put = 0;
|
||||
chan->dma.cur = chan->dma.put;
|
||||
chan->dma.free = chan->dma.max - chan->dma.cur;
|
||||
|
||||
/* Insert NOPS for NOUVEAU_DMA_SKIPS */
|
||||
ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
|
||||
OUT_RING(chan, 0);
|
||||
|
||||
/* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
|
||||
ret = RING_SPACE(chan, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
|
||||
OUT_RING(chan, NvM2MF);
|
||||
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
|
||||
OUT_RING(chan, NvNotify0);
|
||||
|
||||
/* Sit back and pray the channel works.. */
|
||||
FIRE_RING(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
|
||||
{
|
||||
bool is_iomem;
|
||||
u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);
|
||||
mem = &mem[chan->dma.cur];
|
||||
if (is_iomem)
|
||||
memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);
|
||||
else
|
||||
memcpy(mem, data, nr_dwords * 4);
|
||||
chan->dma.cur += nr_dwords;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
READ_GET(struct nouveau_channel *chan, uint32_t *get)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = nvchan_rd32(chan, chan->user_get);
|
||||
if (val < chan->pushbuf_base ||
|
||||
val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) {
|
||||
/* meaningless to dma_wait() except to know whether the
|
||||
* GPU has stalled or not
|
||||
*/
|
||||
*get = val;
|
||||
return false;
|
||||
}
|
||||
|
||||
*get = (val - chan->pushbuf_base) >> 2;
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_dma_wait(struct nouveau_channel *chan, int size)
|
||||
{
|
||||
uint32_t get, prev_get = 0, cnt = 0;
|
||||
bool get_valid;
|
||||
|
||||
while (chan->dma.free < size) {
|
||||
/* reset counter as long as GET is still advancing, this is
|
||||
* to avoid misdetecting a GPU lockup if the GPU happens to
|
||||
* just be processing an operation that takes a long time
|
||||
*/
|
||||
get_valid = READ_GET(chan, &get);
|
||||
if (get != prev_get) {
|
||||
prev_get = get;
|
||||
cnt = 0;
|
||||
}
|
||||
|
||||
if ((++cnt & 0xff) == 0) {
|
||||
DRM_UDELAY(1);
|
||||
if (cnt > 100000)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* loop until we have a usable GET pointer. the value
|
||||
* we read from the GPU may be outside the main ring if
|
||||
* PFIFO is processing a buffer called from the main ring,
|
||||
* discard these values until something sensible is seen.
|
||||
*
|
||||
* the other case we discard GET is while the GPU is fetching
|
||||
* from the SKIPS area, so the code below doesn't have to deal
|
||||
* with some fun corner cases.
|
||||
*/
|
||||
if (!get_valid || get < NOUVEAU_DMA_SKIPS)
|
||||
continue;
|
||||
|
||||
if (get <= chan->dma.cur) {
|
||||
/* engine is fetching behind us, or is completely
|
||||
* idle (GET == PUT) so we have free space up until
|
||||
* the end of the push buffer
|
||||
*
|
||||
* we can only hit that path once per call due to
|
||||
* looping back to the beginning of the push buffer,
|
||||
* we'll hit the fetching-ahead-of-us path from that
|
||||
* point on.
|
||||
*
|
||||
* the *one* exception to that rule is if we read
|
||||
* GET==PUT, in which case the below conditional will
|
||||
* always succeed and break us out of the wait loop.
|
||||
*/
|
||||
chan->dma.free = chan->dma.max - chan->dma.cur;
|
||||
if (chan->dma.free >= size)
|
||||
break;
|
||||
|
||||
/* not enough space left at the end of the push buffer,
|
||||
* instruct the GPU to jump back to the start right
|
||||
* after processing the currently pending commands.
|
||||
*/
|
||||
OUT_RING(chan, chan->pushbuf_base | 0x20000000);
|
||||
WRITE_PUT(NOUVEAU_DMA_SKIPS);
|
||||
|
||||
/* we're now submitting commands at the start of
|
||||
* the push buffer.
|
||||
*/
|
||||
chan->dma.cur =
|
||||
chan->dma.put = NOUVEAU_DMA_SKIPS;
|
||||
}
|
||||
|
||||
/* engine fetching ahead of us, we have space up until the
|
||||
* current GET pointer. the "- 1" is to ensure there's
|
||||
* space left to emit a jump back to the beginning of the
|
||||
* push buffer if we require it. we can never get GET == PUT
|
||||
* here, so this is safe.
|
||||
*/
|
||||
chan->dma.free = get - chan->dma.cur - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user