1/*-
2 * Copyright (c) 2012 Justin Hibbits
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD$");
29
30#include <sys/param.h>
31#include <sys/bus.h>
32#include <sys/systm.h>
33#include <sys/module.h>
34#include <sys/kernel.h>
35#include <sys/rman.h>
36#include <sys/sysctl.h>
37
38#include <machine/bus.h>
39
40#include <dev/ofw/openfirm.h>
41#include <dev/pci/pcivar.h>
42
43#ifndef PCI_VENDOR_ID_ATI
44#define PCI_VENDOR_ID_ATI 0x1002
45#endif
46
47/* From the xf86-video-ati driver's radeon_reg.h */
48#define RADEON_LVDS_GEN_CNTL         0x02d0
49#define  RADEON_LVDS_ON               (1   <<  0)
50#define  RADEON_LVDS_DISPLAY_DIS      (1   <<  1)
51#define  RADEON_LVDS_PANEL_TYPE       (1   <<  2)
52#define  RADEON_LVDS_PANEL_FORMAT     (1   <<  3)
53#define  RADEON_LVDS_RST_FM           (1   <<  6)
54#define  RADEON_LVDS_EN               (1   <<  7)
55#define  RADEON_LVDS_BL_MOD_LEVEL_SHIFT 8
56#define  RADEON_LVDS_BL_MOD_LEVEL_MASK (0xff << 8)
57#define  RADEON_LVDS_BL_MOD_EN        (1   << 16)
58#define  RADEON_LVDS_DIGON            (1   << 18)
59#define  RADEON_LVDS_BLON             (1   << 19)
60#define RADEON_LVDS_PLL_CNTL         0x02d4
61#define  RADEON_LVDS_PLL_EN           (1   << 16)
62#define  RADEON_LVDS_PLL_RESET        (1   << 17)
63#define RADEON_PIXCLKS_CNTL          0x002d
64#define  RADEON_PIXCLK_LVDS_ALWAYS_ONb (1   << 14)
65#define RADEON_DISP_PWR_MAN          0x0d08
66#define  RADEON_AUTO_PWRUP_EN          (1 << 26)
67#define RADEON_CLOCK_CNTL_DATA       0x000c
68#define RADEON_CLOCK_CNTL_INDEX      0x0008
69#define  RADEON_PLL_WR_EN              (1 << 7)
70#define RADEON_CRTC_GEN_CNTL         0x0050
71
72struct atibl_softc {
73	struct resource *sc_memr;
74	int		 sc_level;
75};
76
77static void atibl_identify(driver_t *driver, device_t parent);
78static int atibl_probe(device_t dev);
79static int atibl_attach(device_t dev);
80static int atibl_setlevel(struct atibl_softc *sc, int newlevel);
81static int atibl_getlevel(struct atibl_softc *sc);
82static int atibl_resume(device_t dev);
83static int atibl_suspend(device_t dev);
84static int atibl_sysctl(SYSCTL_HANDLER_ARGS);
85
86static device_method_t atibl_methods[] = {
87	/* Device interface */
88	DEVMETHOD(device_identify,	atibl_identify),
89	DEVMETHOD(device_probe,		atibl_probe),
90	DEVMETHOD(device_attach,	atibl_attach),
91	DEVMETHOD(device_suspend,	atibl_suspend),
92	DEVMETHOD(device_resume,	atibl_resume),
93	{0, 0},
94};
95
96static driver_t	atibl_driver = {
97	"backlight",
98	atibl_methods,
99	sizeof(struct atibl_softc)
100};
101
102static devclass_t atibl_devclass;
103
104DRIVER_MODULE(atibl, vgapci, atibl_driver, atibl_devclass, 0, 0);
105
106static void
107atibl_identify(driver_t *driver, device_t parent)
108{
109	if (OF_finddevice("mac-io/backlight") == -1)
110		return;
111	if (device_find_child(parent, "backlight", -1) == NULL)
112		device_add_child(parent, "backlight", -1);
113}
114
115static int
116atibl_probe(device_t dev)
117{
118	char		control[8];
119	phandle_t	handle;
120
121	handle = OF_finddevice("mac-io/backlight");
122
123	if (handle == -1)
124		return (ENXIO);
125
126	if (OF_getprop(handle, "backlight-control", &control, sizeof(control)) < 0)
127		return (ENXIO);
128
129	if (strcmp(control, "ati") != 0 &&
130	    (strcmp(control, "mnca") != 0 ||
131	    pci_get_vendor(device_get_parent(dev)) != 0x1002))
132		return (ENXIO);
133
134	device_set_desc(dev, "PowerBook backlight for ATI graphics");
135
136	return (0);
137}
138
139static int
140atibl_attach(device_t dev)
141{
142	struct atibl_softc	*sc;
143	struct sysctl_ctx_list *ctx;
144	struct sysctl_oid *tree;
145	int			 rid;
146
147	sc = device_get_softc(dev);
148
149	rid = 0x18;	/* BAR[2], for the MMIO register */
150	sc->sc_memr = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
151			RF_ACTIVE | RF_SHAREABLE);
152	if (sc->sc_memr == NULL) {
153		device_printf(dev, "Could not alloc mem resource!\n");
154		return (ENXIO);
155	}
156
157	ctx = device_get_sysctl_ctx(dev);
158	tree = device_get_sysctl_tree(dev);
159
160	SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
161	    "level", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
162	    atibl_sysctl, "I", "Backlight level (0-100)");
163
164	return (0);
165}
166
167static uint32_t __inline
168atibl_pll_rreg(struct atibl_softc *sc, uint32_t reg)
169{
170	uint32_t data, save, tmp;
171
172	bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, (reg & 0x3f));
173	(void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
174	(void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL);
175
176	data = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
177
178	/* Only necessary on R300, but won't hurt others. */
179	save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX);
180	tmp = save & (~0x3f | RADEON_PLL_WR_EN);
181	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp);
182	tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
183	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save);
184
185	return data;
186}
187
188static void __inline
189atibl_pll_wreg(struct atibl_softc *sc, uint32_t reg, uint32_t val)
190{
191	uint32_t save, tmp;
192
193	bus_write_1(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX,
194	    ((reg & 0x3f) | RADEON_PLL_WR_EN));
195	(void)bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
196	(void)bus_read_4(sc->sc_memr, RADEON_CRTC_GEN_CNTL);
197
198	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA, val);
199	DELAY(5000);
200
201	/* Only necessary on R300, but won't hurt others. */
202	save = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX);
203	tmp = save & (~0x3f | RADEON_PLL_WR_EN);
204	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, tmp);
205	tmp = bus_read_4(sc->sc_memr, RADEON_CLOCK_CNTL_DATA);
206	bus_write_4(sc->sc_memr, RADEON_CLOCK_CNTL_INDEX, save);
207}
208
209static int
210atibl_setlevel(struct atibl_softc *sc, int newlevel)
211{
212	uint32_t lvds_gen_cntl;
213	uint32_t lvds_pll_cntl;
214	uint32_t pixclks_cntl;
215	uint32_t disp_pwr_reg;
216
217	if (newlevel > 100)
218		newlevel = 100;
219
220	if (newlevel < 0)
221		newlevel = 0;
222
223	lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL);
224
225	if (newlevel > 0) {
226		newlevel = (newlevel * 5) / 2 + 5;
227		disp_pwr_reg = bus_read_4(sc->sc_memr, RADEON_DISP_PWR_MAN);
228		disp_pwr_reg |= RADEON_AUTO_PWRUP_EN;
229		bus_write_4(sc->sc_memr, RADEON_DISP_PWR_MAN, disp_pwr_reg);
230		lvds_pll_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL);
231		lvds_pll_cntl |= RADEON_LVDS_PLL_EN;
232		bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl);
233		lvds_pll_cntl &= ~RADEON_LVDS_PLL_RESET;
234		bus_write_4(sc->sc_memr, RADEON_LVDS_PLL_CNTL, lvds_pll_cntl);
235		DELAY(1000);
236
237		lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS |
238		    RADEON_LVDS_BL_MOD_LEVEL_MASK);
239		lvds_gen_cntl |= RADEON_LVDS_ON | RADEON_LVDS_EN |
240		    RADEON_LVDS_DIGON | RADEON_LVDS_BLON;
241		lvds_gen_cntl |= (newlevel << RADEON_LVDS_BL_MOD_LEVEL_SHIFT) &
242		    RADEON_LVDS_BL_MOD_LEVEL_MASK;
243		lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN;
244		DELAY(200000);
245		bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
246	} else {
247		pixclks_cntl = atibl_pll_rreg(sc, RADEON_PIXCLKS_CNTL);
248		atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL,
249		    pixclks_cntl & ~RADEON_PIXCLK_LVDS_ALWAYS_ONb);
250		lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
251		lvds_gen_cntl &= ~(RADEON_LVDS_BL_MOD_EN | RADEON_LVDS_BL_MOD_LEVEL_MASK);
252		bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
253		lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN);
254		DELAY(200000);
255		bus_write_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
256
257		atibl_pll_wreg(sc, RADEON_PIXCLKS_CNTL, pixclks_cntl);
258		DELAY(200000);
259	}
260
261	return (0);
262}
263
264static int
265atibl_getlevel(struct atibl_softc *sc)
266{
267	uint32_t	lvds_gen_cntl;
268	int			level;
269
270	lvds_gen_cntl = bus_read_4(sc->sc_memr, RADEON_LVDS_GEN_CNTL);
271
272	level = ((lvds_gen_cntl & RADEON_LVDS_BL_MOD_LEVEL_MASK) >>
273	    RADEON_LVDS_BL_MOD_LEVEL_SHIFT);
274	if (level != 0)
275		level = ((level - 5) * 2) / 5;
276
277	return (level);
278}
279
280static int
281atibl_suspend(device_t dev)
282{
283	struct atibl_softc *sc;
284
285	sc = device_get_softc(dev);
286
287	sc->sc_level = atibl_getlevel(sc);
288	atibl_setlevel(sc, 0);
289
290	return (0);
291}
292
293static int
294atibl_resume(device_t dev)
295{
296	struct atibl_softc *sc;
297
298	sc = device_get_softc(dev);
299
300	atibl_setlevel(sc, sc->sc_level);
301
302	return (0);
303}
304
305static int
306atibl_sysctl(SYSCTL_HANDLER_ARGS)
307{
308	struct atibl_softc *sc;
309	int newlevel, error;
310
311	sc = arg1;
312
313	newlevel = atibl_getlevel(sc);
314
315	error = sysctl_handle_int(oidp, &newlevel, 0, req);
316
317	if (error || !req->newptr)
318		return (error);
319
320	return (atibl_setlevel(sc, newlevel));
321}
322