/*-
* Copyright (c) 2006 Itronix Inc.
* All rights reserved.
*
* Written by Garrett D'Amore for Itronix Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of Itronix Inc. may not be used to endorse
* or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* ATI Technologies Inc. ("ATI") has not assisted in the creation of, and
* does not endorse, this software. ATI will not be responsible or liable
* for any actual or alleged damage or loss caused by or in connection with
* the use of or reliance on this software.
*/
/*
* Portions of this code were taken from XFree86's Radeon driver, which bears
* this notice:
*
* Copyright 2000 ATI Technologies Inc., Markham, Ontario, and
* VA Linux Systems Inc., Fremont, California.
*
* 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 on 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
* NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
* THEIR 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.
*/
/* acceleration support */
static void radeonfb_rectfill(struct radeonfb_display *, int dstx, int dsty,
int width, int height, uint32_t color);
static void radeonfb_rectfill_a(void *, int, int, int, int, long);
static void radeonfb_bitblt(void *, int srcx, int srcy,
int dstx, int dsty, int width, int height, int rop);
/*
* Some flags are general to entire chip families, and rather
* than clutter up the table with them, we go ahead and set
* them here.
*/
switch (sc->sc_family) {
case RADEON_RS100:
case RADEON_RS200:
sc->sc_flags |= RFB_IGP | RFB_RV100;
break;
case RADEON_RV100:
case RADEON_RV200:
case RADEON_RV250:
case RADEON_RV280:
sc->sc_flags |= RFB_RV100;
break;
case RADEON_RS300:
sc->sc_flags |= RFB_SDAC | RFB_IGP | RFB_RV100;
break;
case RADEON_R300:
case RADEON_RV350:
case RADEON_R350:
case RADEON_RV380:
case RADEON_R420:
case RADEON_R580:
/* newer chips */
sc->sc_flags |= RFB_R300;
break;
case RADEON_R100:
sc->sc_flags |= RFB_NCRTC2;
break;
}
PRINTREG(RADEON_CRTC_EXT_CNTL);
PRINTREG(RADEON_CRTC_GEN_CNTL);
PRINTREG(RADEON_CRTC2_GEN_CNTL);
PRINTREG(RADEON_DISP_OUTPUT_CNTL);
PRINTREG(RADEON_DAC_CNTL2);
PRINTREG(RADEON_BIOS_4_SCRATCH);
PRINTREG(RADEON_FP_GEN_CNTL);
sc->sc_fp_gen_cntl = GET32(sc, RADEON_FP_GEN_CNTL);
PRINTREG(RADEON_FP2_GEN_CNTL);
PRINTREG(RADEON_TMDS_CNTL);
PRINTREG(RADEON_TMDS_TRANSMITTER_CNTL);
PRINTREG(RADEON_TMDS_PLL_CNTL);
PRINTREG(RADEON_LVDS_GEN_CNTL);
PRINTREG(RADEON_DISP_HW_DEBUG);
if (!IS_AVIVO(sc)) {
/*
XXX: We can't print this, as it's not correctly aligned
PRINTREG(RADEON_PIXCLKS_CNTL);
*/
PRINTREG(RADEON_CRTC_H_SYNC_STRT_WID);
PRINTREG(RADEON_FP_H_SYNC_STRT_WID);
PRINTREG(RADEON_CRTC2_H_SYNC_STRT_WID);
PRINTREG(RADEON_FP_H2_SYNC_STRT_WID);
}
/*
* XXX
* This was if (IS_RV100()), which is set for all pre-R3xx chips.
* I suspect this only makes sense on Sun XVR-100 with firmware that doesn't
* support DVI, so for now let's restrict it to only actual RV100
*/
if (sc->sc_family == RADEON_RV100)
PUT32(sc, RADEON_TMDS_PLL_CNTL, 0xa27);
/* XXX
* according to xf86-video-radeon R3xx has this bit backwards
*/
if (IS_R300(sc)) {
PATCH32(sc, RADEON_TMDS_TRANSMITTER_CNTL,
0,
~(RADEON_TMDS_TRANSMITTER_PLLEN | RADEON_TMDS_TRANSMITTER_PLLRST));
} else {
PATCH32(sc, RADEON_TMDS_TRANSMITTER_CNTL,
RADEON_TMDS_TRANSMITTER_PLLEN,
~(RADEON_TMDS_TRANSMITTER_PLLEN | RADEON_TMDS_TRANSMITTER_PLLRST));
}
/* 64 MB should be enough -- more just wastes map entries */
if (sc->sc_memsz > (64 << 20))
sc->sc_memsz = (64 << 20);
for (i = 0; radeonfb_limits[i].size; i++) {
if (sc->sc_memsz >= radeonfb_limits[i].size) {
sc->sc_maxx = radeonfb_limits[i].maxx;
sc->sc_maxy = radeonfb_limits[i].maxy;
sc->sc_maxbpp = radeonfb_limits[i].maxbpp;
/* framebuffer offset, start at a 4K page */
sc->sc_fboffset = sc->sc_memsz /
radeonfb_limits[i].maxdisp;
/*
* we use the fbsize to figure out where we can store
* things like cursor data.
*/
sc->sc_fbsize =
ROUNDUP(ROUNDUP(sc->sc_maxx * sc->sc_maxbpp / 8 ,
RADEON_STRIDEALIGN) * sc->sc_maxy,
4096);
break;
}
}
radeonfb_init_misc(sc);
/* program the DAC wirings */
for (i = 0; i < (HAS_CRTC2(sc) ? 2 : 1); i++) {
switch (sc->sc_ports[i].rp_dac_type) {
case RADEON_DAC_PRIMARY:
PATCH32(sc, RADEON_DAC_CNTL2,
i ? RADEON_DAC2_DAC_CLK_SEL : 0,
~RADEON_DAC2_DAC_CLK_SEL);
break;
case RADEON_DAC_TVDAC:
/* we always use the TVDAC to drive a secondary analog
* CRT for now. if we ever support TV-out this will
* have to change.
*/
SET32(sc, RADEON_DAC_CNTL2,
RADEON_DAC2_DAC2_CLK_SEL);
PATCH32(sc, RADEON_DISP_HW_DEBUG,
i ? 0 : RADEON_CRT2_DISP1_SEL,
~RADEON_CRT2_DISP1_SEL);
/* we're using CRTC2 for the 2nd port */
if (sc->sc_ports[i].rp_number == 1) {
PATCH32(sc, RADEON_DISP_OUTPUT_CNTL,
RADEON_DISP_DAC2_SOURCE_CRTC2,
~RADEON_DISP_DAC2_SOURCE_MASK);
}
break;
}
DPRINTF(("%s: port %d tmds type %d\n", __func__, i,
sc->sc_ports[i].rp_tmds_type));
switch (sc->sc_ports[i].rp_tmds_type) {
case RADEON_TMDS_INT:
/* point FP0 at the CRTC this port uses */
DPRINTF(("%s: plugging internal TMDS into CRTC %d\n",
__func__, sc->sc_ports[i].rp_number));
if (IS_R300(sc)) {
PATCH32(sc, RADEON_FP_GEN_CNTL,
sc->sc_ports[i].rp_number ?
R200_FP_SOURCE_SEL_CRTC2 :
R200_FP_SOURCE_SEL_CRTC1,
~R200_FP_SOURCE_SEL_MASK);
} else {
PATCH32(sc, RADEON_FP_GEN_CNTL,
sc->sc_ports[i].rp_number ?
RADEON_FP_SEL_CRTC2 :
RADEON_FP_SEL_CRTC1,
~RADEON_FP_SEL_MASK);
}
break;
case RADEON_TMDS_EXT:
/* point FP2 at the CRTC this port uses */
DPRINTF(("%s: plugging external TMDS into CRTC %d\n",
__func__, sc->sc_ports[i].rp_number));
if (IS_R300(sc)) {
PATCH32(sc, RADEON_FP2_GEN_CNTL,
sc->sc_ports[i].rp_number ?
R200_FP2_SOURCE_SEL_CRTC2 :
R200_FP2_SOURCE_SEL_CRTC1,
~R200_FP2_SOURCE_SEL_CRTC2);
} else {
PATCH32(sc, RADEON_FP2_GEN_CNTL,
sc->sc_ports[i].rp_number ?
RADEON_FP2_SRC_SEL_CRTC2 :
RADEON_FP2_SRC_SEL_CRTC1,
~RADEON_FP2_SRC_SEL_CRTC2);
}
break;
}
}
PRINTREG(RADEON_DAC_CNTL2);
PRINTREG(RADEON_DISP_HW_DEBUG);
PRINTREG(RADEON_DAC_CNTL);
/* other DAC programming */
v = GET32(sc, RADEON_DAC_CNTL);
v &= (RADEON_DAC_RANGE_CNTL_MASK | RADEON_DAC_BLANKING);
v |= RADEON_DAC_MASK_ALL | RADEON_DAC_8BIT_EN;
PUT32(sc, RADEON_DAC_CNTL, v);
PRINTREG(RADEON_DAC_CNTL);
/* XXX: this may need more investigation */
PUT32(sc, RADEON_TV_DAC_CNTL, 0x00280203);
PRINTREG(RADEON_TV_DAC_CNTL);
/* enable TMDS */
SET32(sc, RADEON_FP_GEN_CNTL,
RADEON_FP_TMDS_EN |
RADEON_FP_CRTC_DONT_SHADOW_VPAR |
RADEON_FP_CRTC_DONT_SHADOW_HEND);
/*
* XXX
* no idea why this is necessary - if I do not clear this bit on my
* iBook G4 the screen remains black, even though it's already clear.
* It needs to be set on my Sun XVR-100 for the DVI port to work
* TODO:
* see if this is still necessary now that CRTCs, DACs and outputs are
* getting wired up in a halfway sane way
*/
if (sc->sc_fp_gen_cntl & RADEON_FP_SEL_CRTC2) {
SET32(sc, RADEON_FP_GEN_CNTL, RADEON_FP_SEL_CRTC2);
} else {
CLR32(sc, RADEON_FP_GEN_CNTL, RADEON_FP_SEL_CRTC2);
}
/*
* we use bus_space_map instead of pci_mapreg, because we don't
* need the full aperature space. no point in wasting virtual
* address space we don't intend to use, right?
*/
if ((sc->sc_memsz < (4096 * 1024)) ||
(pci_mapreg_info(sc->sc_pc, sc->sc_pt, RADEON_MAPREG_VRAM,
PCI_MAPREG_TYPE_MEM, &sc->sc_memaddr, &bsz, NULL) != 0) ||
(bsz < sc->sc_memsz)) {
sc->sc_memsz = 0;
aprint_error("%s: Bad frame buffer configuration\n",
XNAME(sc));
goto error;
}
/* initialize some basic display parameters */
for (i = 0; i < sc->sc_ndisplays; i++) {
struct radeonfb_display *dp = &sc->sc_displays[i];
struct rasops_info *ri;
long defattr;
struct wsemuldisplaydev_attach_args aa;
/*
* Figure out how many "displays" (desktops) we are going to
* support. If more than one, then each CRTC gets its own
* programming.
*
* XXX: this code needs to change to support mergedfb.
* XXX: would be nice to allow this to be overridden
*/
if (HAS_CRTC2(sc) && (sc->sc_ndisplays == 1)) {
DPRINTF(("dual crtcs!\n"));
dp->rd_ncrtcs = 2;
dp->rd_crtcs[0].rc_port =
&sc->sc_ports[0];
dp->rd_crtcs[0].rc_number = sc->sc_ports[0].rp_number;
dp->rd_crtcs[1].rc_port =
&sc->sc_ports[1];
dp->rd_crtcs[1].rc_number = sc->sc_ports[1].rp_number;
} else {
dp->rd_ncrtcs = 1;
dp->rd_crtcs[0].rc_port =
&sc->sc_ports[i];
dp->rd_crtcs[0].rc_number = sc->sc_ports[i].rp_number;
}
/* for text mode, we pick a resolution that won't
* require panning */
radeonfb_pickres(dp, &dp->rd_virtx, &dp->rd_virty, 0);
aprint_normal("%s: display %d: "
"initial virtual resolution %dx%d at %d bpp\n",
XNAME(sc), i, dp->rd_virtx, dp->rd_virty, dp->rd_bpp);
aprint_normal_dev(sc->sc_dev, "using %d MB per display\n",
sc->sc_fboffset >> 20);
/* now select the *video mode* that we will use */
for (j = 0; j < dp->rd_ncrtcs; j++) {
const struct videomode *vmp;
vmp = radeonfb_port_mode(sc, dp->rd_crtcs[j].rc_port,
dp->rd_virtx, dp->rd_virty);
/*
* virtual resolution should be at least as high as
* physical
*/
if (dp->rd_virtx < vmp->hdisplay ||
dp->rd_virty < vmp->vdisplay) {
dp->rd_virtx = vmp->hdisplay;
dp->rd_virty = vmp->vdisplay;
}
/*
* since we're not the console we can postpone
* the rest until someone actually allocates a
* screen for us. but we do clear the screen
* at least.
*/
memset(ri->ri_bits, 0, 1024);
#ifdef SPLASHSCREEN
if (splash_render(&dp->rd_splash,
SPLASH_F_CENTER|SPLASH_F_FILL) == 0)
SCREEN_DISABLE_DRAWING(&dp->rd_vscreen);
#endif
}
/*
* if we attach a DRM we need to unmap registers in
* WSDISPLAYIO_MODE_MAPPED, since this keeps us from doing things like
* screen blanking we only do it if needed
*/
sc->sc_needs_unmap =
(config_found(dev, aux, radeonfb_drm_print,
CFARGS(.iattr = "drm")) != NULL);
DPRINTF(("needs_unmap: %d\n", sc->sc_needs_unmap));
if (!IS_AVIVO(sc)) {
PRINTREG(RADEON_CRTC_EXT_CNTL);
PRINTREG(RADEON_CRTC_GEN_CNTL);
PRINTREG(RADEON_CRTC2_GEN_CNTL);
PRINTREG(RADEON_DISP_OUTPUT_CNTL);
PRINTREG(RADEON_DAC_CNTL2);
PRINTREG(RADEON_FP_GEN_CNTL);
PRINTREG(RADEON_FP2_GEN_CNTL);
PRINTREG(RADEON_TMDS_CNTL);
PRINTREG(RADEON_TMDS_TRANSMITTER_CNTL);
PRINTREG(RADEON_TMDS_PLL_CNTL);
/*
XXX: We can't print this, as it's not correctly aligned
PRINTREG(RADEON_PIXCLKS_CNTL);
*/
}
return;
error:
if (sc->sc_biossz)
free(sc->sc_bios, M_DEVBUF);
if (sc->sc_regsz)
bus_space_unmap(sc->sc_regt, sc->sc_regh, sc->sc_regsz);
if (sc->sc_memsz)
bus_space_unmap(sc->sc_memt, sc->sc_memh, sc->sc_memsz);
}
/* can't do these without registers being mapped */
if (!sc->sc_mapped) {
switch (cmd) {
case WSDISPLAYIO_GVIDEO:
case WSDISPLAYIO_SVIDEO:
case WSDISPLAYIO_GETCMAP:
case WSDISPLAYIO_PUTCMAP:
case WSDISPLAYIO_SCURSOR:
case WSDISPLAYIO_GCURPOS:
case WSDISPLAYIO_SCURPOS:
case WSDISPLAYIO_SETPARAM:
return EINVAL;
}
}
/*
* restrict all other mappings to processes with superuser privileges
* or the kernel itself
*/
if (kauth_authorize_machdep(kauth_cred_get(), KAUTH_MACHDEP_UNMANAGEDMEM,
NULL, NULL, NULL, NULL) != 0) {
aprint_error_dev(sc->sc_dev, "mmap() rejected.\n");
return -1;
}
/* unmap the PCI expansion rom */
bus_space_unmap(romt, romh, romsz);
/* turn off rom decoder now */
pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM,
pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_MAPREG_ROM) &
~PCI_MAPREG_ROM_ENABLE);
if (refclk && refdiv && minpll && maxpll)
goto dontprobe;
if (!sc->sc_biossz) {
/* no BIOS */
aprint_verbose("%s: No video BIOS, using default clocks\n",
XNAME(sc));
if (IS_IGP(sc))
refclk = refclk ? refclk : 1432;
else
refclk = refclk ? refclk : 2700;
refdiv = refdiv ? refdiv : 12;
minpll = minpll ? minpll : 12500;
/* XXX
* Need to check if the firmware or something programmed a
* higher value than this, and if so, bump it.
* The RV280 in my iBook is unhappy if the PLL input is less
* than 360MHz
*/
maxpll = maxpll ? maxpll : 40000/*35000*/;
} else if (IS_ATOM(sc)) {
/* ATOM BIOS */
ptr = GETBIOS16(sc, 0x48);
ptr = GETBIOS16(sc, ptr + 32); /* aka MasterDataStart */
ptr = GETBIOS16(sc, ptr + 12); /* pll info block */
refclk = refclk ? refclk : GETBIOS16(sc, ptr + 82);
minpll = minpll ? minpll : GETBIOS16(sc, ptr + 78);
maxpll = maxpll ? maxpll : GETBIOS16(sc, ptr + 32);
/*
* ATOM BIOS doesn't supply a reference divider, so we
* have to probe for it.
*/
if (refdiv < 2)
refdiv = GETPLL(sc, RADEON_PPLL_REF_DIV) &
RADEON_PPLL_REF_DIV_MASK;
/*
* if probe is zero, just assume one that should work
* for most parts
*/
if (refdiv < 2)
refdiv = 12;
int
radeonfb_calc_dividers(struct radeonfb_softc *sc, uint32_t dotclock,
uint32_t *postdivbit, uint32_t *feedbackdiv, int flags)
{
int i;
uint32_t outfreq;
int div;
DPRINTF(("dot clock: %u\n", dotclock));
for (i = 0; (div = radeonfb_dividers[i].divider) != 0; i++) {
/*
* XXX
* the rv350 in my last generation 14" iBook G4 produces
* garbage with dividers > 4. No idea if this is a hardware
* limitation or an error in the divider table.
*/
if ((sc->sc_family == RADEON_RV350) && (div > 4))
continue;
int
radeonfb_getconnectors(struct radeonfb_softc *sc)
{
int i;
int found = 0;
for (i = 0; i < 2; i++) {
sc->sc_ports[i].rp_mon_type = RADEON_MT_UNKNOWN;
sc->sc_ports[i].rp_ddc_type = RADEON_DDC_NONE;
sc->sc_ports[i].rp_dac_type = RADEON_DAC_UNKNOWN;
sc->sc_ports[i].rp_conn_type = RADEON_CONN_NONE;
sc->sc_ports[i].rp_tmds_type = RADEON_TMDS_UNKNOWN;
}
/*
* This logic is borrowed from Xorg's radeon driver.
*/
if (!sc->sc_biossz)
goto nobios;
if (IS_ATOM(sc)) {
/* not done yet */
} else {
uint16_t ptr;
int port = 0;
ptr = GETBIOS16(sc, 0x48);
ptr = GETBIOS16(sc, ptr + 0x50);
for (i = 1; i < 4; i++) {
uint16_t entry;
uint8_t conn, ddc, dac, tmds;
/*
* Parse the connector table. From reading the code,
* it appears to made up of 16-bit entries for each
* connector. The 16-bits are defined as:
*
* bits 12-15 - connector type (0 == end of table)
* bits 8-11 - DDC type
* bits 5-7 - ???
* bit 4 - TMDS type (1 = EXT, 0 = INT)
* bits 1-3 - ???
* bit 0 - DAC, 1 = TVDAC, 0 = primary
*/
if (!GETBIOS8(sc, ptr + i * 2) && i > 1)
break;
entry = GETBIOS16(sc, ptr + i * 2);
if (conn == RADEON_CONN_NONE)
continue; /* no connector */
/*
* XXX
* both Mac Mini variants have both outputs wired to
* the same connector and share the DDC lines
*/
if ((found > 0) &&
(sc->sc_ports[port].rp_ddc_type == ddc)) {
/* duplicate entry for same connector */
continue;
}
/* internal DDC_DVI port gets priority */
if ((ddc == RADEON_DDC_DVI) || (port == 1))
port = 0;
else
port = 1;
/*
* Fixup for RS300/RS350/RS400 chips, that lack a primary DAC.
* these chips should use TVDAC for the VGA port.
*/
if (HAS_SDAC(sc)) {
if (sc->sc_ports[0].rp_conn_type == RADEON_CONN_CRT) {
sc->sc_ports[0].rp_dac_type = RADEON_DAC_TVDAC;
sc->sc_ports[1].rp_dac_type = RADEON_DAC_PRIMARY;
} else {
sc->sc_ports[1].rp_dac_type = RADEON_DAC_TVDAC;
sc->sc_ports[0].rp_dac_type = RADEON_DAC_PRIMARY;
}
} else if (!HAS_CRTC2(sc)) {
sc->sc_ports[0].rp_dac_type = RADEON_DAC_PRIMARY;
}
for (i = 0; i < 2; i++) {
char edid[128], edid_port_str[7] = "EDID:";
uint8_t ddc;
struct edid_info *eip = &sc->sc_ports[i].rp_edid;
prop_data_t edid_data;
sc->sc_ports[i].rp_edid_valid = 0;
/*
* First look for static EDID data
* Try "EDID:port" then "EDID"
*/
snprintf(&edid_port_str[5], 2, "%d", i);
edid_data = prop_dictionary_get(device_properties(
sc->sc_dev), edid_port_str);
if (edid_data == NULL)
edid_data = prop_dictionary_get(device_properties(
sc->sc_dev), "EDID");
if (edid_data != NULL) {
aprint_debug_dev(sc->sc_dev, "using static EDID\n");
memcpy(edid, prop_data_value(edid_data), 128);
if (edid_parse(edid, eip) == 0) {
sc->sc_ports[i].rp_edid_valid = 1;
#ifdef RADEONFB_DEBUG
edid_print(eip);
#endif
}
}
/* if we didn't find any we'll try to talk to the monitor */
if (sc->sc_ports[i].rp_edid_valid != 1) {
ddc = sc->sc_ports[i].rp_ddc_type;
if (ddc != RADEON_DDC_NONE) {
if ((radeonfb_i2c_read_edid(sc, ddc, edid)
== 0) && (edid_parse(edid, eip) == 0)) {
const struct videomode *
radeonfb_modelookup(const char *name)
{
int i;
/* Use a default mode in case we don't find a matching mode */
const char *vm = "1024x768x60";
const struct videomode *vmp = NULL;
for (i = 0; i < videomode_count; i++) {
if (!strcmp(name, videomode_list[i].name))
return &videomode_list[i];
if (!strcmp(vm, videomode_list[i].name))
vmp = &videomode_list[i];
}
return vmp;
}
void
radeonfb_pllwriteupdate(struct radeonfb_softc *sc, int crtc)
{
if (crtc) {
while (GETPLL(sc, RADEON_P2PLL_REF_DIV) &
RADEON_P2PLL_ATOMIC_UPDATE_R);
SETPLL(sc, RADEON_P2PLL_REF_DIV, RADEON_P2PLL_ATOMIC_UPDATE_W);
} else {
while (GETPLL(sc, RADEON_PPLL_REF_DIV) &
RADEON_PPLL_ATOMIC_UPDATE_R);
SETPLL(sc, RADEON_PPLL_REF_DIV, RADEON_PPLL_ATOMIC_UPDATE_W);
}
}
void
radeonfb_pllwaitatomicread(struct radeonfb_softc *sc, int crtc)
{
int i;
for (i = 10000; i; i--) {
if (crtc) {
if (GETPLL(sc, RADEON_P2PLL_REF_DIV) &
RADEON_P2PLL_ATOMIC_UPDATE_R)
break;
} else {
if (GETPLL(sc, RADEON_PPLL_REF_DIV) &
RADEON_PPLL_ATOMIC_UPDATE_R)
break;
}
}
}
void
radeonfb_program_vclk(struct radeonfb_softc *sc, int dotclock, int crtc, int flags)
{
uint32_t pbit = 0;
uint32_t feed = 0;
uint32_t data, refdiv, div0, r2xxref;
/*
* XXX
* the RV350 in my last generation iBook G4 behaves like an
* r2xx here - try to detect that and not screw up the reference
* divider.
* xf86-video-radeon just skips PLL programming altogether
* on iBooks, probably for this reason.
*/
r2xxref = (refdiv & ~RADEON_PPLL_REF_DIV_MASK) | sc->sc_refdiv;
if (IS_R300(sc) && (r2xxref != refdiv)) {
refdiv = (refdiv & ~R300_PPLL_REF_DIV_ACC_MASK) |
(sc->sc_refdiv << R300_PPLL_REF_DIV_ACC_SHIFT);
} else {
refdiv = (refdiv & ~RADEON_PPLL_REF_DIV_MASK) |
sc->sc_refdiv;
}
DPRINTF(("refdiv %08x\n", refdiv));
div0 = GETPLL(sc, RADEON_PPLL_DIV_0);
DPRINTF(("div0 %08x\n", div0));
div0 &= ~(RADEON_PPLL_FB3_DIV_MASK |
RADEON_PPLL_POST3_DIV_MASK);
div0 |= pbit;
div0 |= (feed & RADEON_PPLL_FB3_DIV_MASK);
DPRINTF(("div0 %08x\n", div0));
if ((refdiv == GETPLL(sc, RADEON_PPLL_REF_DIV)) &&
(div0 == GETPLL(sc, RADEON_PPLL_DIV_0))) {
/*
* nothing to do here, the PLL is already where we
* want it
*/
PATCH32(sc, RADEON_CLOCK_CNTL_INDEX, 0,
~RADEON_PLL_DIV_SEL);
aprint_debug_dev(sc->sc_dev, "no need to touch the PLL\n");
return;
}
/* alright, we do need to reprogram stuff */
PATCHPLL(sc, RADEON_VCLK_ECP_CNTL,
RADEON_VCLK_SRC_SEL_CPUCLK,
~RADEON_VCLK_SRC_SEL_MASK);
/* put vclk into reset, use atomic updates */
SETPLL(sc, RADEON_PPLL_CNTL,
RADEON_PPLL_REFCLK_SEL |
RADEON_PPLL_FBCLK_SEL |
RADEON_PPLL_RESET |
RADEON_PPLL_ATOMIC_UPDATE_EN |
RADEON_PPLL_VGA_ATOMIC_UPDATE_EN);
/* put vclk into reset, use atomic updates */
SETPLL(sc, RADEON_P2PLL_CNTL,
RADEON_P2PLL_RESET |
RADEON_P2PLL_ATOMIC_UPDATE_EN |
RADEON_P2PLL_VGA_ATOMIC_UPDATE_EN);
/* program reference divider */
PATCHPLL(sc, RADEON_P2PLL_REF_DIV, sc->sc_refdiv,
~RADEON_P2PLL_REF_DIV_MASK);
/* program feedback and post dividers */
data = GETPLL(sc, RADEON_P2PLL_DIV_0);
data &= ~(RADEON_P2PLL_FB0_DIV_MASK |
RADEON_P2PLL_POST0_DIV_MASK);
data |= pbit;
data |= (feed & RADEON_P2PLL_FB0_DIV_MASK);
PUTPLL(sc, RADEON_P2PLL_DIV_0, data);
PUTPLL(sc, RADEON_P2PLL_DIV_0, data);
if (IS_AVIVO(sc)) {
/*
* no actual mode setting yet, we just make sure the CRTCs
* point at the right memory ranges and use the same pitch
* for the drawing engine
*/
if (GET32(sc, AVIVO_D1CRTC_CONTROL) & AVIVO_CRTC_EN) {
CLR32(sc, AVIVO_D1GRPH_CONTROL, AVIVO_D1GRPH_MACRO_ADDRESS_MODE);
dp->rd_stride = GET32(sc, AVIVO_D1GRPH_PITCH);
PUT32(sc, AVIVO_D1GRPH_PRIMARY_SURFACE_ADDRESS, 0);
}
if (GET32(sc, AVIVO_D2CRTC_CONTROL) & AVIVO_CRTC_EN) {
CLR32(sc, AVIVO_D2GRPH_CONTROL, AVIVO_D1GRPH_MACRO_ADDRESS_MODE);
dp->rd_stride = GET32(sc, AVIVO_D2GRPH_PITCH);
PUT32(sc, AVIVO_D2GRPH_PRIMARY_SURFACE_ADDRESS, 0);
}
return;
}
/* blank the display while we switch modes */
//radeonfb_blank(dp, 1);
/*
* CRTC_EXT_CNTL - preserve disable flags, set ATI linear and EXT_CNT
*/
v = GET32(sc, RADEON_CRTC_EXT_CNTL);
if (crtc == 0) {
v &= (RADEON_CRTC_VSYNC_DIS | RADEON_CRTC_HSYNC_DIS |
RADEON_CRTC_DISPLAY_DIS);
v |= RADEON_XCRT_CNT_EN | RADEON_VGA_ATI_LINEAR;
if (mode->flags & VID_CSYNC)
v |= RADEON_CRTC_VSYNC_TRISTAT;
}
/* unconditional turn on CRT, in case first CRTC is DFP */
v |= RADEON_CRTC_CRT_ON;
PUT32(sc, RADEON_CRTC_EXT_CNTL, v);
PRINTREG(RADEON_CRTC_EXT_CNTL);
void
radeonfb_blank(struct radeonfb_display *dp, int blank)
{
struct radeonfb_softc *sc = dp->rd_softc;
uint32_t reg, mask;
uint32_t fpreg, fpval;
int i;
if (!sc->sc_mapped)
return;
if(IS_AVIVO(sc)) {
/*
* XXX
* I don't know how to turn the sunc outputs off for DPMS
* power control, so for now just turn the entire CRTC off
*/
if (blank) {
CLR32(sc, AVIVO_D1CRTC_CONTROL, AVIVO_CRTC_EN);
CLR32(sc, AVIVO_D2CRTC_CONTROL, AVIVO_CRTC_EN);
} else {
SET32(sc, AVIVO_D1CRTC_CONTROL, AVIVO_CRTC_EN);
SET32(sc, AVIVO_D2CRTC_CONTROL, AVIVO_CRTC_EN);
}
return;
}
/* non-AVIVO case */
for (i = 0; i < dp->rd_ncrtcs; i++) {
/* enable acceleration */
dp->rd_putchar = ri->ri_ops.putchar;
ri->ri_ops.copyrows = radeonfb_copyrows;
ri->ri_ops.copycols = radeonfb_copycols;
ri->ri_ops.eraserows = radeonfb_eraserows;
ri->ri_ops.erasecols = radeonfb_erasecols;
/* pick a putchar method based on font and Radeon model */
if (ri->ri_font->stride < ri->ri_font->fontwidth) {
/* got a bitmap font */
#if !defined(RADEONFB_ALWAYS_ACCEL_PUTCHAR)
if (IS_R300(dp->rd_softc) && 0) {
/*
* radeonfb_putchar() doesn't work right on some R3xx
* so we use software drawing here, the wrapper just
* makes sure the engine is idle before scribbling
* into vram
*/
ri->ri_ops.putchar = radeonfb_putchar_wrapper;
} else
#endif
ri->ri_ops.putchar = radeonfb_putchar;
} else {
/* got an alpha font */
switch(ri->ri_depth) {
case 32:
ri->ri_ops.putchar = radeonfb_putchar_aa32;
break;
case 8:
ri->ri_ops.putchar = radeonfb_putchar_aa8;
break;
default:
/* XXX this should never happen */
panic("%s: depth is not 8 or 32 but we got an" \
" alpha font?!", __func__);
}
}
ri->ri_ops.cursor = radeonfb_cursor;
}
static void
radeonfb_putpal(struct radeonfb_display *dp, int idx, int r, int g, int b)
{
struct radeonfb_softc *sc = dp->rd_softc;
int crtc, cc;
uint32_t vclk;
if (IS_AVIVO(sc)) {
for (cc = 0; cc < dp->rd_ncrtcs; cc++) {
crtc = dp->rd_crtcs[cc].rc_number;
if (crtc)
PUT32(sc, AVIVO_DC_LUT_RW_SELECT, 1);
else
PUT32(sc, AVIVO_DC_LUT_RW_SELECT, 0);
for (i = 0; i <= CLUT_WIDTH; ++i) {
tmp = i & 0xe0;
/*
* replicate bits so 0xe0 maps to a red value of 0xff
* in order to make white look actually white
*/
tmp |= (tmp >> 3) | (tmp >> 6);
r = tmp;
tmp = (i & 0x1c) << 3;
tmp |= (tmp >> 3) | (tmp >> 6);
g = tmp;
tmp = (i & 0x03) << 6;
tmp |= tmp >> 2;
tmp |= tmp >> 4;
b = tmp;
dp->rd_cmap_red[i] = r;
dp->rd_cmap_green[i] = g;
dp->rd_cmap_blue[i] = b;
radeonfb_putpal(dp, i, r, g, b);
}
} else {
/* linear ramp */
for (i = 0; i <= CLUT_WIDTH; ++i) {
radeonfb_putpal(dp, i, i, i, i);
}
}
}
static int
radeonfb_putcmap(struct radeonfb_display *dp, struct wsdisplay_cmap *cm)
{
u_char *r, *g, *b;
u_int index = cm->index;
u_int count = cm->count;
int i, error;
u_char rbuf[256], gbuf[256], bbuf[256];
/*
* I doubt we can upload data faster than even the slowest Radeon
* could process them, especially when doing the alpha blending stuff
* along the way, so just make sure there's some room in the FIFO and
* then hammer away
* As it turns out we can, so make periodic stops to let the FIFO
* drain.
*/
radeonfb_wait_fifo(sc, 20);
for (i = 0; i < ri->ri_fontscale; i++) {
aval = *data;
data++;
if (aval == 0) {
pixel = bg;
} else if (aval == 255) {
pixel = fg;
} else {
r = aval * rf + (255 - aval) * rb;
g = aval * gf + (255 - aval) * gb;
b = aval * bf + (255 - aval) * bb;
pixel = (r & 0xff00) << 8 |
(g & 0xff00) |
(b & 0xff00) >> 8;
}
if (i & 16)
radeonfb_wait_fifo(sc, 20);
PUT32(sc, RADEON_HOST_DATA0, pixel);
}
if (rv == GC_ADD) {
glyphcache_add(&dp->rd_gc, c, xd, yd);
} else if (attr & 1)
radeonfb_rectfill(dp, xd, yd + h - 2, w, 1, fg);
}
static void
radeonfb_putchar_aa8(void *cookie, int row, int col, u_int c, long attr)
{
struct rasops_info *ri = cookie;
struct vcons_screen *scr = ri->ri_hw;
struct radeonfb_display *dp = scr->scr_cookie;
struct radeonfb_softc *sc = dp->rd_softc;
struct wsdisplay_font *font = PICK_FONT(ri, c);
uint32_t bg, fg, latch = 0, bg8, fg8, pixel, gmc;
int i, x, y, wi, he, r, g, b, aval;
int r1, g1, b1, r0, g0, b0, fgo, bgo;
uint8_t *data8;
int rv, cnt;
if (dp->rd_wsmode != WSDISPLAYIO_MODE_EMUL)
return;
static void
radeonfb_eraserows(void *cookie, int row, int nrows, long fillattr)
{
struct rasops_info *ri = cookie;
struct vcons_screen *scr = ri->ri_hw;
struct radeonfb_display *dp = scr->scr_cookie;
uint32_t x, y, w, h, fg, bg, ul;
/* XXX: check for full emulation mode? */
if (dp->rd_wsmode == WSDISPLAYIO_MODE_EMUL) {
x = ri->ri_xorigin;
y = ri->ri_yorigin + ri->ri_font->fontheight * row;
w = ri->ri_emuwidth;
h = ri->ri_font->fontheight * nrows;
static void
radeonfb_cursor(void *cookie, int on, int row, int col)
{
struct rasops_info *ri = cookie;
struct vcons_screen *scr = ri->ri_hw;
struct radeonfb_display *dp = scr->scr_cookie;
int x, y, wi, he;
wi = ri->ri_font->fontwidth;
he = ri->ri_font->fontheight;
if (dp->rd_wsmode == WSDISPLAYIO_MODE_EMUL) {
x = ri->ri_ccol * wi + ri->ri_xorigin;
y = ri->ri_crow * he + ri->ri_yorigin;
/* first turn off the old cursor */
if (ri->ri_flg & RI_CURSOR) {
radeonfb_bitblt(dp, x, y, x, y, wi, he,
RADEON_ROP3_Dn);
ri->ri_flg &= ~RI_CURSOR;
}
ri->ri_crow = row;
ri->ri_ccol = col;
/* then (possibly) turn on the new one */
if (on) {
x = ri->ri_ccol * wi + ri->ri_xorigin;
y = ri->ri_crow * he + ri->ri_yorigin;
radeonfb_bitblt(dp, x, y, x, y, wi, he,
RADEON_ROP3_Dn);
ri->ri_flg |= RI_CURSOR;
}
} else {
scr->scr_ri.ri_crow = row;
scr->scr_ri.ri_ccol = col;
scr->scr_ri.ri_flg &= ~RI_CURSOR;
}
}
/*
* Underlying acceleration support.
*/
static void
radeonfb_rectfill(struct radeonfb_display *dp, int dstx, int dsty,
int width, int height, uint32_t color)
{
struct radeonfb_softc *sc = dp->rd_softc;
uint32_t gmc;
static void
radeonfb_bitblt(void *cookie, int srcx, int srcy,
int dstx, int dsty, int width, int height, int rop)
{
struct radeonfb_display *dp = cookie;
struct radeonfb_softc *sc = dp->rd_softc;
uint32_t gmc;
uint32_t dir;
if (dsty < srcy) {
dir = RADEON_DST_Y_TOP_TO_BOTTOM;
} else {
srcy += height - 1;
dsty += height - 1;
dir = 0;
}
if (dstx < srcx) {
dir |= RADEON_DST_X_LEFT_TO_RIGHT;
} else {
srcx += width - 1;
dstx += width - 1;
}
/*
* Apple OF hands us some radeons with tiling enabled - explicitly
* disable it here
*/
PUT32(sc, RADEON_SURFACE_CNTL, RADEON_SURF_TRANSLATION_DIS);
radeonfb_wait_fifo(sc, 1);
if (!IS_R300(sc) && !IS_AVIVO(sc))
PUT32(sc, RADEON_RB2D_DSTCACHE_MODE, 0);
radeonfb_wait_fifo(sc, 3);
/*
* XXX
* I strongly suspect this works mostly by accident on !AVIVO
* AVIVO uses all 22 bits for the framebuffer offset, so it can
* address up to 4GB. Older chips probably use bits 20-22 for other
* things and we just so happen to set the right ones by having our
* PCI/AGP space above 0x80000000.
* Either way, r5xx does not work if we set these bits, while older
* chips don't work without.
*/
pitch = (dp->rd_stride + 0x3f) >> 6;
if (IS_AVIVO(sc)) {
pitch = pitch << 22;
} else
pitch = (pitch << 22) | (sc->sc_aperbase >> 10);
/*
* According to comments in XFree code, resetting the HDP via
* the RBBM_SOFT_RESET can cause bad behavior on some systems.
* So we use HOST_PATH_CNTL instead.
*/
hpc = GET32(sc, RADEON_HOST_PATH_CNTL);
rbbm = GET32(sc, RADEON_RBBM_SOFT_RESET);
if (IS_R300(sc) || IS_AVIVO(sc)) {
PUT32(sc, RADEON_RBBM_SOFT_RESET, rbbm |
RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_HI |
RADEON_SOFT_RESET_E2);
GET32(sc, RADEON_RBBM_SOFT_RESET);
PUT32(sc, RADEON_RBBM_SOFT_RESET, 0);
/*
* XXX: this bit is not defined in any ATI docs I have,
* nor in the XFree code, but XFree does it. Why?
*/
SET32(sc, RADEON_RB2D_DSTCACHE_MODE, R300_DC_DC_DISABLE_IGNORE_PE);
} else {
PUT32(sc, RADEON_RBBM_SOFT_RESET, rbbm |
RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_SE |
RADEON_SOFT_RESET_RE |
RADEON_SOFT_RESET_PP |
RADEON_SOFT_RESET_E2 |
RADEON_SOFT_RESET_RB);
GET32(sc, RADEON_RBBM_SOFT_RESET);
PUT32(sc, RADEON_RBBM_SOFT_RESET, rbbm &
~(RADEON_SOFT_RESET_CP |
RADEON_SOFT_RESET_SE |
RADEON_SOFT_RESET_RE |
RADEON_SOFT_RESET_PP |
RADEON_SOFT_RESET_E2 |
RADEON_SOFT_RESET_RB));
GET32(sc, RADEON_RBBM_SOFT_RESET);
}
if (HAS_R300CG(sc))
radeonfb_r300cg_workaround(sc);
}
static int
radeonfb_set_curpos(struct radeonfb_display *dp, struct wsdisplay_curpos *pos)
{
int x, y;
x = pos->x;
y = pos->y;
/*
* This doesn't let a cursor move off the screen. I'm not
* sure if this will have negative effects for e.g. Xinerama.
* I'd guess Xinerama handles it by changing the cursor shape,
* but that needs verification.
*/
if (x >= dp->rd_virtx)
x = dp->rd_virtx - 1;
if (x < 0)
x = 0;
if (y >= dp->rd_virty)
y = dp->rd_virty - 1;
if (y < 0)
y = 0;
l = d << 7;
l |= ((d & 0x02) << 5);
l |= ((d & 0x04) << 3);
l |= ((d & 0x08) << 1);
l |= ((d & 0x10) >> 1);
l |= ((d & 0x20) >> 3);
l |= ((d & 0x40) >> 5);
l |= ((d & 0x80) >> 7);
return l;
}
/*
* Change the cursor shape. Call this with the cursor locked to avoid
* flickering/tearing.
*/
static void
radeonfb_cursor_shape(struct radeonfb_display *dp)
{
uint8_t and[512], xor[512];
int i, j, src, dst /* , pitch */;
const uint8_t *msk = dp->rd_cursor.rc_mask;
const uint8_t *img = dp->rd_cursor.rc_image;
/*
* Radeon cursor data interleaves one line of AND data followed
* by a line of XOR data. (Each line corresponds to a whole hardware
* pitch - i.e. 64 pixels or 8 bytes.)
*
* The cursor is displayed using the following table:
*
* AND XOR Result
* ----------------------
* 0 0 Cursor color 0
* 0 1 Cursor color 1
* 1 0 Transparent
* 1 1 Complement of background
*
* Our masks are therefore different from what we were passed.
* Passed in, I'm assuming the data represents either color 0 or 1,
* and a mask, so the passed in table looks like:
*
* IMG Mask Result
* -----------------------
* 0 0 Transparent
* 0 1 Cursor color 0
* 1 0 Transparent
* 1 1 Cursor color 1
*
* IF mask bit == 1, AND = 0, XOR = color.
* IF mask bit == 0, AND = 1, XOR = 0.
*
* hence: AND = ~(mask); XOR = color & ~(mask);
*/
/* start by assuming all bits are transparent */
memset(and, 0xff, 512);
memset(xor, 0x00, 512);
src = 0;
dst = 0;
for (i = 0; i < 64; i++) {
for (j = 0; j < 64; j += 8) {
if ((i < dp->rd_cursor.rc_size.y) &&
(j < dp->rd_cursor.rc_size.x)) {
/* take care to leave odd bits alone */
and[dst] &= ~(msk[src]);
xor[dst] = img[src] & msk[src];
src++;
}
dst++;
}
}
for (i = 0; i < 512; i++) {
and[i] = radeonfb_backwards(and[i]);
xor[i] = radeonfb_backwards(xor[i]);
}
/* copy the image into place */
for (i = 0; i < 64; i++) {
memcpy((uint8_t *)dp->rd_curptr + (i * 16),
&and[i * 8], 8);
memcpy((uint8_t *)dp->rd_curptr + (i * 16) + 8,
&xor[i * 8], 8);
}
}
/*
* We use the cursor in 24bit mode on avivo, much simpler than the above.
* Should probably do the same on older radeons
*/
static void
radeonfb_avivo_cursor_shape(struct radeonfb_display *dp)
{
const uint8_t *msk = dp->rd_cursor.rc_mask;
const uint8_t *img = dp->rd_cursor.rc_image;
uint32_t *out = (uint32_t *)dp->rd_curptr;
uint8_t bit;
int i, j, px;
for (i = 0; i < 64 * 8; i++) {
bit = 0x01;
for (j = 0; j < 8; j++) {
px = ((*msk & bit) ? 2 : 0) | ((*img & bit) ? 1 : 0);
switch (px) {
case 0:
case 1:
*out = htole32(0x00000000);
break;
case 2:
*out = htole32(0xff000000 |
dp->rd_cursor.rc_cmap[0]);
break;
case 3:
*out = htole32(0xff000000 |
dp->rd_cursor.rc_cmap[1]);
break;
}
out++;
bit = bit << 1;
}
msk++;
img++;
}
}
x = dp->rd_cursor.rc_pos.x;
y = dp->rd_cursor.rc_pos.y;
while (y < rcp->rc_yoffset) {
rcp->rc_yoffset -= RADEON_PANINCREMENT;
}
while (y >= (rcp->rc_yoffset + rcp->rc_videomode.vdisplay)) {
rcp->rc_yoffset += RADEON_PANINCREMENT;
}
while (x < rcp->rc_xoffset) {
rcp->rc_xoffset -= RADEON_PANINCREMENT;
}
while (x >= (rcp->rc_xoffset + rcp->rc_videomode.hdisplay)) {
rcp->rc_xoffset += RADEON_PANINCREMENT;
}
/* adjust for the cursor's hotspot */
x -= dp->rd_cursor.rc_hot.x;
y -= dp->rd_cursor.rc_hot.y;
xoff = yoff = 0;
if (x >= dp->rd_virtx)
x = dp->rd_virtx - 1;
if (y >= dp->rd_virty)
y = dp->rd_virty - 1;
/* now adjust cursor so it is relative to viewport */
x -= rcp->rc_xoffset;
y -= rcp->rc_yoffset;
/*
* no need to check for fall off, because we should
* never move off the screen entirely!
*/
coff = 0;
if (x < 0) {
xoff = -x;
x = 0;
}
if (y < 0) {
yoff = -y;
y = 0;
coff = (yoff * 2) * 8;
}
/* pan the display */
if (crtcoff != 0)
PUT32(sc, crtcoff, (rcp->rc_yoffset * dp->rd_stride) +
rcp->rc_xoffset);
for (i = 0; i < dp->rd_ncrtcs; i++) {
if (dp->rd_crtcs[i].rc_number) {
if (IS_AVIVO(sc)) {
SET32(sc, AVIVO_D2CUR_UPDATE, AVIVO_D1CURSOR_UPDATE_LOCK);
} else
SET32(sc, RADEON_CUR2_OFFSET, RADEON_CUR_LOCK);
} else {
if (IS_AVIVO(sc)) {
SET32(sc, AVIVO_D1CUR_UPDATE, AVIVO_D1CURSOR_UPDATE_LOCK);
} else
SET32(sc, RADEON_CUR_OFFSET,RADEON_CUR_LOCK);
}
}
if (which & WSDISPLAY_CURSOR_DOCMAP) {
if (IS_AVIVO(sc)) {
/*
* we use an ARGB cursor here, so we need to rebuild
* the cursor image every time the palette changes
*/
radeonfb_avivo_cursor_shape(dp);
} else
radeonfb_cursor_cmap(dp);
}
if (which & WSDISPLAY_CURSOR_DOSHAPE) {
if (IS_AVIVO(sc)) {
radeonfb_avivo_cursor_shape(dp);
} else
radeonfb_cursor_shape(dp);
}
if (which & WSDISPLAY_CURSOR_DOCUR)
radeonfb_cursor_visible(dp);
/* this one is unconditional, because it updates other stuff */
radeonfb_cursor_position(dp);
}
static const struct videomode *
radeonfb_port_mode(struct radeonfb_softc *sc, struct radeonfb_port *rp,
int x, int y)
{
struct edid_info *ep = &rp->rp_edid;
struct videomode *vmp = NULL;
int i;
if (!rp->rp_edid_valid) {
/* fallback to safe mode */
return radeonfb_modelookup(sc->sc_defaultmode);
}
/* always choose the preferred mode first! */
if (ep->edid_preferred_mode) {
/* XXX: add auto-stretching support for native mode */
/* this may want panning to occur, btw */
if ((ep->edid_preferred_mode->hdisplay <= x) &&
(ep->edid_preferred_mode->vdisplay <= y))
return ep->edid_preferred_mode;
}
for (i = 0; i < ep->edid_nmodes; i++) {
/*
* We elect to pick a resolution that is too large for
* the monitor than one that is too small. This means
* that we will prefer to pan rather than to try to
* center a smaller display on a larger screen. In
* practice, this shouldn't matter because if a
* monitor can support a larger resolution, it can
* probably also support the smaller. A specific
* exception is fixed format panels, but hopefully
* they are properly dealt with by the "autostretch"
* logic above.
*/
if ((ep->edid_modes[i].hdisplay > x) ||
(ep->edid_modes[i].vdisplay > y)) {
continue;
}
/*
* at this point, the display mode is no larger than
* what we've requested.
*/
if (vmp == NULL)
vmp = &ep->edid_modes[i];
static int
radeonfb_hasres(struct videomode *list, int nlist, int x, int y)
{
int i;
for (i = 0; i < nlist; i++) {
if ((x == list[i].hdisplay) &&
(y == list[i].vdisplay)) {
return 1;
}
}
return 0;
}
static void
radeonfb_pickres(struct radeonfb_display *dp, uint16_t *x, uint16_t *y,
int pan)
{
struct radeonfb_port *rp;
struct edid_info *ep;
int i, j;
*x = 0;
*y = 0;
if (pan) {
for (i = 0; i < dp->rd_ncrtcs; i++) {
rp = dp->rd_crtcs[i].rc_port;
ep = &rp->rp_edid;
if (!rp->rp_edid_valid) {
/* monitor not present */
continue;
}
/*
* For now we are ignoring "conflict" that
* could occur when mixing some modes like
* 1280x1024 and 1400x800. It isn't clear
* which is better, so the first one wins.
*/
for (j = 0; j < ep->edid_nmodes; j++) {
/*
* ignore resolutions that are too big for
* the radeon
*/
if (ep->edid_modes[j].hdisplay >
dp->rd_softc->sc_maxx)
continue;
if (ep->edid_modes[j].vdisplay >
dp->rd_softc->sc_maxy)
continue;
/*
* pick largest resolution, the
* smaller monitor will pan
*/
if ((ep->edid_modes[j].hdisplay >= *x) &&
(ep->edid_modes[j].vdisplay >= *y)) {
*x = ep->edid_modes[j].hdisplay;
*y = ep->edid_modes[j].vdisplay;
}
}
}
} else {
struct videomode *modes;
size_t smodes;
int nmodes = 0;
int valid = 0;
for (i = 0; i < dp->rd_ncrtcs; i++) {
/*
* pick the largest resolution in common.
*/
rp = dp->rd_crtcs[i].rc_port;
ep = &rp->rp_edid;
if (!rp->rp_edid_valid)
continue;
if (!valid) {
/*
* Pick the preferred mode for this port
* if available.
*/
if (ep->edid_preferred_mode) {
struct videomode *vmp =
ep->edid_preferred_mode;
/* initialize starting list */
for (j = 0; j < ep->edid_nmodes; j++) {
/*
* ignore resolutions that are
* too big for the radeon
*/
if (ep->edid_modes[j].hdisplay >
dp->rd_softc->sc_maxx)
continue;
if (ep->edid_modes[j].vdisplay >
dp->rd_softc->sc_maxy)
continue;
/* now we have to pick from the merged list */
for (i = 0; i < nmodes; i++) {
if ((modes[i].hdisplay >= *x) &&
(modes[i].vdisplay >= *y)) {
*x = modes[i].hdisplay;
*y = modes[i].vdisplay;
}
}
kmem_free(modes, smodes);
/*
* On some chips, we should negate the backlight level.
* XXX Find out on which chips.
*/
if (dp->rd_softc->sc_flags & RFB_INV_BLIGHT)
level = RADEONFB_BACKLIGHT_MAX - level;
splx(s);
return level;
}
/* Set the backlight to the given level for the display. */
static void
radeonfb_switch_backlight(struct radeonfb_display *dp, int on)
{
if (dp->rd_bl_on == on)
return;
dp->rd_bl_on = on;
radeonfb_set_backlight(dp, dp->rd_bl_level);
}
static int
radeonfb_set_backlight(struct radeonfb_display *dp, int level)
{
struct radeonfb_softc *sc = dp->rd_softc;
int rlevel, s;
uint32_t lvds;
if(!sc->sc_mapped)
return 0;
s = spltty();
dp->rd_bl_level = level;
if (dp->rd_bl_on == 0)
level = 0;
if (level < 0)
level = 0;
else if (level >= RADEONFB_BACKLIGHT_MAX)
level = RADEONFB_BACKLIGHT_MAX;
/* On some chips, we should negate the backlight level. */
if (dp->rd_softc->sc_flags & RFB_INV_BLIGHT) {
rlevel = RADEONFB_BACKLIGHT_MAX - level;
} else
rlevel = level;
/*
* Turn off the display if the backlight is set to 0, since the
* display is useless without backlight anyway.
*/
if (level == 0)
radeonfb_blank(dp, 1);
else if (radeonfb_get_backlight(dp) == 0)
radeonfb_blank(dp, 0);
dp->rd_bl_lvds_val &= ~RADEON_LVDS_STATE_MASK;
dp->rd_bl_lvds_val |= lvds & RADEON_LVDS_STATE_MASK;
/* XXX What is the correct delay? */
callout_schedule(&dp->rd_bl_lvds_co, 200 * hz);
splx(s);
return 0;
}
/*
* Callout function for delayed operations on the LVDS_GEN_CNTL register.
* Set the delayed bits in the register, and clear the stored delayed
* value.
*/
/* we assume the main display is the first one - need a better way */
if (sc->sc_ndisplays < 1) return;
/* make sure pushing the hotkeys always has an effect */
dp->rd_bl_on = 1;
level = dp->rd_bl_level;
level = uimin(RADEONFB_BACKLIGHT_MAX, level + 5);
radeonfb_set_backlight(dp, level);
}
/* we assume the main display is the first one - need a better way */
if (sc->sc_ndisplays < 1) return;
/* make sure pushing the hotkeys always has an effect */
dp->rd_bl_on = 1;
level = dp->rd_bl_level;
level = uimax(0, level - 5);
radeonfb_set_backlight(dp, level);
}