1232177Sjhibbits/*-
2232177Sjhibbits * Copyright (c) 2012 Justin Hibbits
3232177Sjhibbits * All rights reserved.
4232177Sjhibbits *
5232177Sjhibbits * Redistribution and use in source and binary forms, with or without
6232177Sjhibbits * modification, are permitted provided that the following conditions
7232177Sjhibbits * are met:
8232177Sjhibbits * 1. Redistributions of source code must retain the above copyright
9232177Sjhibbits *    notice, this list of conditions and the following disclaimer.
10232177Sjhibbits * 2. Redistributions in binary form must reproduce the above copyright
11232177Sjhibbits *    notice, this list of conditions and the following disclaimer in the
12232177Sjhibbits *    documentation and/or other materials provided with the distribution.
13232177Sjhibbits *
14232177Sjhibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15232177Sjhibbits * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16232177Sjhibbits * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17232177Sjhibbits * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18232177Sjhibbits * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19232177Sjhibbits * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20232177Sjhibbits * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21232177Sjhibbits * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22232177Sjhibbits * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23232177Sjhibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24232177Sjhibbits * SUCH DAMAGE.
25232177Sjhibbits */
26232177Sjhibbits
27232177Sjhibbits#include <sys/cdefs.h>
28232177Sjhibbits__FBSDID("$FreeBSD$");
29232177Sjhibbits
30232177Sjhibbits#include <sys/param.h>
31232177Sjhibbits#include <sys/bus.h>
32232177Sjhibbits#include <sys/systm.h>
33232177Sjhibbits#include <sys/module.h>
34232177Sjhibbits#include <sys/kernel.h>
35232177Sjhibbits#include <sys/rman.h>
36232177Sjhibbits#include <sys/sysctl.h>
37232177Sjhibbits
38232177Sjhibbits#include <machine/bus.h>
39232177Sjhibbits
40232177Sjhibbits#include <dev/ofw/openfirm.h>
41232177Sjhibbits
42232177Sjhibbits/* From the xf86-video-ati driver's radeon_reg.h */
43232177Sjhibbits#define RADEON_LVDS_GEN_CNTL         0x02d0
44232177Sjhibbits#define  RADEON_LVDS_ON               (1   <<  0)
45232177Sjhibbits#define  RADEON_LVDS_DISPLAY_DIS      (1   <<  1)
46232177Sjhibbits#define  RADEON_LVDS_PANEL_TYPE       (1   <<  2)
47232177Sjhibbits#define  RADEON_LVDS_PANEL_FORMAT     (1   <<  3)
48232177Sjhibbits#define  RADEON_LVDS_RST_FM           (1   <<  6)
49232177Sjhibbits#define  RADEON_LVDS_EN               (1   <<  7)
50232177Sjhibbits#define  RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8
51232177Sjhibbits#define  RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8)
52232177Sjhibbits#define  RADEON_LVDS_BL_MOD_EN        (1   << 16)
53232177Sjhibbits#define  RADEON_LVDS_DIGON            (1   << 18)
54232177Sjhibbits#define  RADEON_LVDS_BLON             (1   << 19)
55271205Sjhibbits#define RADEON_LVDS_PLL_CNTL         0x02d4
56271205Sjhibbits#define  RADEON_LVDS_PLL_EN           (1   << 16)
57271205Sjhibbits#define  RADEON_LVDS_PLL_RESET        (1   << 17)
58271205Sjhibbits#define RADEON_PIXCLKS_CNTL          0x002d
59271205Sjhibbits#define  RADEON_PIXCLK_LVDS_ALWAYS_ONb (1   << 14)
60271205Sjhibbits#define RADEON_DISP_PWR_MAN          0x0d08
61271205Sjhibbits#define  RADEON_AUTO_PWRUP_EN          (1 << 26)
62271205Sjhibbits#define RADEON_CLOCK_CNTL_DATA       0x000c
63271205Sjhibbits#define RADEON_CLOCK_CNTL_INDEX      0x0008
64271205Sjhibbits#define  RADEON_PLL_WR_EN              (1 << 7)
65271205Sjhibbits#define RADEON_CRTC_GEN_CNTL         0x0050
66232177Sjhibbits
67232177Sjhibbitsstruct atibl_softc {
68232177Sjhibbits	struct resource *sc_memr;
69271205Sjhibbits	int		 sc_level;
70232177Sjhibbits};
71232177Sjhibbits
72232177Sjhibbitsstatic void atibl_identify(driver_t *driver, device_t parent);
73232177Sjhibbitsstatic int atibl_probe(device_t dev);
74232177Sjhibbitsstatic int atibl_attach(device_t dev);
75232177Sjhibbitsstatic int atibl_setlevel(struct atibl_softc *sc, int newlevel);
76232177Sjhibbitsstatic int atibl_getlevel(struct atibl_softc *sc);
77271205Sjhibbitsstatic int atibl_resume(device_t dev);
78271205Sjhibbitsstatic int atibl_suspend(device_t dev);
79232177Sjhibbitsstatic int atibl_sysctl(SYSCTL_HANDLER_ARGS);
80232177Sjhibbits
81232177Sjhibbitsstatic device_method_t atibl_methods[] = {
82232177Sjhibbits	/* Device interface */
83271205Sjhibbits	DEVMETHOD(device_identify,	atibl_identify),
84271205Sjhibbits	DEVMETHOD(device_probe,		atibl_probe),
85271205Sjhibbits	DEVMETHOD(device_attach,	atibl_attach),
86271205Sjhibbits	DEVMETHOD(device_suspend,	atibl_suspend),
87271205Sjhibbits	DEVMETHOD(device_resume,	atibl_resume),
88232177Sjhibbits	{0, 0},
89232177Sjhibbits};
90232177Sjhibbits
91232177Sjhibbitsstatic driver_t	atibl_driver = {
92232177Sjhibbits	"backlight",
93232177Sjhibbits	atibl_methods,
94232177Sjhibbits	sizeof(struct atibl_softc)
95232177Sjhibbits};
96232177Sjhibbits
97232177Sjhibbitsstatic devclass_t atibl_devclass;
98232177Sjhibbits
99232177SjhibbitsDRIVER_MODULE(atibl, vgapci, atibl_driver, atibl_devclass, 0, 0);
100232177Sjhibbits
101232177Sjhibbitsstatic void
102232177Sjhibbitsatibl_identify(driver_t *driver, device_t parent)
103232177Sjhibbits{
104255100Sjhibbits	if (OF_finddevice("mac-io/backlight") == -1)
105255100Sjhibbits		return;
106232177Sjhibbits	if (device_find_child(parent, "backlight", -1) == NULL)
107232177Sjhibbits		device_add_child(parent, "backlight", -1);
108232177Sjhibbits}
109232177Sjhibbits
110232177Sjhibbitsstatic int
111232177Sjhibbitsatibl_probe(device_t dev)
112232177Sjhibbits{
113232177Sjhibbits	char		control[8];
114232177Sjhibbits	phandle_t	handle;
115232177Sjhibbits
116232177Sjhibbits	handle = OF_finddevice("mac-io/backlight");
117232177Sjhibbits
118239548Sjhibbits	if (handle == -1)
119232177Sjhibbits		return (ENXIO);
120232177Sjhibbits
121232177Sjhibbits	if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0)
122232177Sjhibbits		return (ENXIO);
123232177Sjhibbits
124232177Sjhibbits	if (strcmp(control, "ati") != 0)
125232177Sjhibbits		return (ENXIO);
126232177Sjhibbits
127239548Sjhibbits	device_set_desc(dev, "PowerBook backlight for ATI graphics");
128232177Sjhibbits
129232177Sjhibbits	return (0);
130232177Sjhibbits}
131232177Sjhibbits
132232177Sjhibbitsstatic int
133232177Sjhibbitsatibl_attach(device_t dev)
134232177Sjhibbits{
135232177Sjhibbits	struct atibl_softc	*sc;
136232177Sjhibbits	struct sysctl_ctx_list *ctx;
137232177Sjhibbits	struct sysctl_oid *tree;
138232177Sjhibbits	int			 rid;
139232177Sjhibbits
140232177Sjhibbits	sc = device_get_softc(dev);
141232177Sjhibbits
142232177Sjhibbits	rid = 0x18;	/* BAR[2], for the MMIO register */
143232177Sjhibbits	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
144232177Sjhibbits			RF_ACTIVE | RF_SHAREABLE);
145232177Sjhibbits	if (sc->sc_memr == NULL) {
146232177Sjhibbits		device_printf(dev, "Could not alloc mem resource!\n");
147232177Sjhibbits		return (ENXIO);
148232177Sjhibbits	}
149232177Sjhibbits
150232177Sjhibbits	ctx = device_get_sysctl_ctx(dev);
151232177Sjhibbits	tree = device_get_sysctl_tree(dev);
152232177Sjhibbits
153232177Sjhibbits	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
154271205Sjhibbits	    "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
155271205Sjhibbits	    atibl_sysctl, "I", "Backlight level (0-100)");
156232177Sjhibbits
157232177Sjhibbits	return (0);
158232177Sjhibbits}
159232177Sjhibbits
160271205Sjhibbitsstatic uint32_t __inline
161271205Sjhibbitsatibl_pll_rreg(struct atibl_softc *sc, uint32_t reg)
162271205Sjhibbits{
163271205Sjhibbits	uint32_t data, save, tmp;
164271205Sjhibbits
165271205Sjhibbits	bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX,
166271205Sjhibbits	    ((reg & 0x3f) | RADEON_PLL_WR_EN));
167271205Sjhibbits	(void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
168271205Sjhibbits	(void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL);
169271205Sjhibbits
170271205Sjhibbits	data = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
171271205Sjhibbits
172271205Sjhibbits	/* Only necessary on R300, bt won't hurt others. */
173271205Sjhibbits	save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX);
174271205Sjhibbits	tmp = save & (~0x3f | RADEON_PLL_WR_EN);
175271205Sjhibbits	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp);
176271205Sjhibbits	tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
177271205Sjhibbits	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save);
178271205Sjhibbits
179271205Sjhibbits	return data;
180271205Sjhibbits}
181271205Sjhibbits
182271205Sjhibbitsstatic void __inline
183271205Sjhibbitsatibl_pll_wreg(struct atibl_softc *sc, uint32_t reg, uint32_t val)
184271205Sjhibbits{
185271205Sjhibbits	uint32_t save, tmp;
186271205Sjhibbits
187271205Sjhibbits	bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX,
188271205Sjhibbits	    ((reg & 0x3f) | RADEON_PLL_WR_EN));
189271205Sjhibbits	(void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
190271205Sjhibbits	(void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL);
191271205Sjhibbits
192271205Sjhibbits	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA, val);
193271205Sjhibbits	DELAY(5000);
194271205Sjhibbits
195271205Sjhibbits	/* Only necessary on R300, bt won't hurt others. */
196271205Sjhibbits	save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX);
197271205Sjhibbits	tmp = save & (~0x3f | RADEON_PLL_WR_EN);
198271205Sjhibbits	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp);
199271205Sjhibbits	tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
200271205Sjhibbits	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save);
201271205Sjhibbits}
202271205Sjhibbits
203232177Sjhibbitsstatic int
204232177Sjhibbitsatibl_setlevel(struct atibl_softc *sc, int newlevel)
205232177Sjhibbits{
206232177Sjhibbits	uint32_t lvds_gen_cntl;
207271205Sjhibbits	uint32_t lvds_pll_cntl;
208271205Sjhibbits	uint32_t pixclks_cntl;
209271205Sjhibbits	uint32_t disp_pwr_reg;
210232177Sjhibbits
211232177Sjhibbits	if (newlevel > 100)
212232177Sjhibbits		newlevel = 100;
213232177Sjhibbits
214232177Sjhibbits	if (newlevel < 0)
215232177Sjhibbits		newlevel = 0;
216232177Sjhibbits
217232177Sjhibbits	lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL);
218232177Sjhibbits
219271205Sjhibbits	if (newlevel > 0) {
220271205Sjhibbits		newlevel = (newlevel * 5) / 2 + 5;
221271205Sjhibbits		disp_pwr_reg = bus_read_4(sc->sc_memr, RADEON_DISP_PWR_MAN);
222271205Sjhibbits		disp_pwr_reg |= RADEON_AUTO_PWRUP_EN;
223271205Sjhibbits		bus_write_4(sc->sc_memr, RADEON_DISP_PWR_MAN, disp_pwr_reg);
224271205Sjhibbits		lvds_pll_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL);
225271205Sjhibbits		lvds_pll_cntl |= RADEON_LVDS_PLL_EN;
226271205Sjhibbits		bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl);
227271205Sjhibbits		lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET;
228271205Sjhibbits		bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl);
229271205Sjhibbits		DELAY(1000);
230271205Sjhibbits
231271205Sjhibbits		lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS |
232271205Sjhibbits		    RADEON_LVDS_BL_MOD_LEVEL_MASK);
233271205Sjhibbits		lvds_gen_cntl |= RADEON_LVDS_ON | RADEON_LVDS_EN |
234271205Sjhibbits		    RADEON_LVDS_DIGON | RADEON_LVDS_BLON;
235271205Sjhibbits		lvds_gen_cntl |= (newlevel << RADEON_LVDS_BL_MOD_LEVEL_SHIFT) &
236271205Sjhibbits		    RADEON_LVDS_BL_MOD_LEVEL_MASK;
237271205Sjhibbits		lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN;
238271205Sjhibbits		DELAY(200000);
239271205Sjhibbits		bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
240271205Sjhibbits	} else {
241271205Sjhibbits		pixclks_cntl = atibl_pll_rreg(sc, RADEON_PIXCLKS_CNTL);
242271205Sjhibbits		atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL,
243271205Sjhibbits		    pixclks_cntl & ~RADEON_PIXCLK_LVDS_ALWAYS_ONb);
244271205Sjhibbits		lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
245271205Sjhibbits		lvds_gen_cntl &= ~(RADEON_LVDS_BL_MOD_EN | RADEON_LVDS_BL_MOD_LEVEL_MASK);
246271205Sjhibbits		bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
247271205Sjhibbits		lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN);
248271205Sjhibbits		DELAY(200000);
249271205Sjhibbits		bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
250271205Sjhibbits
251271205Sjhibbits		atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL, pixclks_cntl);
252271205Sjhibbits		DELAY(200000);
253271205Sjhibbits	}
254271205Sjhibbits
255232177Sjhibbits	return (0);
256232177Sjhibbits}
257232177Sjhibbits
258232177Sjhibbitsstatic int
259232177Sjhibbitsatibl_getlevel(struct atibl_softc *sc)
260232177Sjhibbits{
261232177Sjhibbits	uint32_t	lvds_gen_cntl;
262232177Sjhibbits	int			level;
263232177Sjhibbits
264232177Sjhibbits	lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL);
265232177Sjhibbits
266232177Sjhibbits	level = ((lvds_gen_cntl & RADEON_LVDS_BL_MOD_LEVEL_MASK) >>
267271205Sjhibbits	    RADEON_LVDS_BL_MOD_LEVEL_SHIFT);
268271205Sjhibbits	if (level != 0)
269271205Sjhibbits		level = ((level - 5) * 2) / 5;
270232177Sjhibbits
271232177Sjhibbits	return (level);
272232177Sjhibbits}
273232177Sjhibbits
274232177Sjhibbitsstatic int
275271205Sjhibbitsatibl_suspend(device_t dev)
276271205Sjhibbits{
277271205Sjhibbits	struct atibl_softc *sc;
278271205Sjhibbits
279271205Sjhibbits	sc = device_get_softc(dev);
280271205Sjhibbits
281271205Sjhibbits	sc->sc_level = atibl_getlevel(sc);
282271205Sjhibbits	atibl_setlevel(sc, 0);
283271205Sjhibbits
284271205Sjhibbits	return (0);
285271205Sjhibbits}
286271205Sjhibbits
287271205Sjhibbitsstatic int
288271205Sjhibbitsatibl_resume(device_t dev)
289271205Sjhibbits{
290271205Sjhibbits	struct atibl_softc *sc;
291271205Sjhibbits
292271205Sjhibbits	sc = device_get_softc(dev);
293271205Sjhibbits
294271205Sjhibbits	atibl_setlevel(sc, sc->sc_level);
295271205Sjhibbits
296271205Sjhibbits	return (0);
297271205Sjhibbits}
298271205Sjhibbits
299271205Sjhibbitsstatic int
300232177Sjhibbitsatibl_sysctl(SYSCTL_HANDLER_ARGS)
301232177Sjhibbits{
302232177Sjhibbits	struct atibl_softc *sc;
303232177Sjhibbits	int newlevel, error;
304232177Sjhibbits
305232177Sjhibbits	sc = arg1;
306232177Sjhibbits
307232177Sjhibbits	newlevel = atibl_getlevel(sc);
308232177Sjhibbits
309232177Sjhibbits	error = sysctl_handle_int(oidp, &newlevel, 0, req);
310232177Sjhibbits
311232177Sjhibbits	if (error || !req->newptr)
312232177Sjhibbits		return (error);
313232177Sjhibbits
314232177Sjhibbits	return (atibl_setlevel(sc, newlevel));
315232177Sjhibbits}
316