1 /* 2 * Driver for Samsung Q10 and related laptops: controls the backlight 3 * 4 * Copyright (c) 2011 Frederick van der Wyck <fvanderwyck@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 */ 11 12 #include <linux/module.h> 13 #include <linux/kernel.h> 14 #include <linux/init.h> 15 #include <linux/platform_device.h> 16 #include <linux/backlight.h> 17 #include <linux/i8042.h> 18 #include <linux/dmi.h> 19 20 #define SAMSUNGQ10_BL_MAX_INTENSITY 255 21 #define SAMSUNGQ10_BL_DEFAULT_INTENSITY 185 22 23 #define SAMSUNGQ10_BL_8042_CMD 0xbe 24 #define SAMSUNGQ10_BL_8042_DATA { 0x89, 0x91 } 25 26 static int samsungq10_bl_brightness; 27 28 static bool force; 29 module_param(force, bool, 0); 30 MODULE_PARM_DESC(force, 31 "Disable the DMI check and force the driver to be loaded"); 32 33 static int samsungq10_bl_set_intensity(struct backlight_device *bd) 34 { 35 36 int brightness = bd->props.brightness; 37 unsigned char c[3] = SAMSUNGQ10_BL_8042_DATA; 38 39 c[2] = (unsigned char)brightness; 40 i8042_lock_chip(); 41 i8042_command(c, (0x30 << 8) | SAMSUNGQ10_BL_8042_CMD); 42 i8042_unlock_chip(); 43 samsungq10_bl_brightness = brightness; 44 45 return 0; 46 } 47 48 static int samsungq10_bl_get_intensity(struct backlight_device *bd) 49 { 50 return samsungq10_bl_brightness; 51 } 52 53 static const struct backlight_ops samsungq10_bl_ops = { 54 .get_brightness = samsungq10_bl_get_intensity, 55 .update_status = samsungq10_bl_set_intensity, 56 }; 57 58 #ifdef CONFIG_PM_SLEEP 59 static int samsungq10_suspend(struct device *dev) 60 { 61 return 0; 62 } 63 64 static int samsungq10_resume(struct device *dev) 65 { 66 67 struct backlight_device *bd = dev_get_drvdata(dev); 68 69 samsungq10_bl_set_intensity(bd); 70 return 0; 71 } 72 #else 73 #define samsungq10_suspend NULL 74 #define samsungq10_resume NULL 75 #endif 76 77 static SIMPLE_DEV_PM_OPS(samsungq10_pm_ops, 78 samsungq10_suspend, samsungq10_resume); 79 80 static int __devinit samsungq10_probe(struct platform_device *pdev) 81 { 82 83 struct backlight_properties props; 84 struct backlight_device *bd; 85 86 memset(&props, 0, sizeof(struct backlight_properties)); 87 props.type = BACKLIGHT_PLATFORM; 88 props.max_brightness = SAMSUNGQ10_BL_MAX_INTENSITY; 89 bd = backlight_device_register("samsung", &pdev->dev, NULL, 90 &samsungq10_bl_ops, &props); 91 if (IS_ERR(bd)) 92 return PTR_ERR(bd); 93 94 platform_set_drvdata(pdev, bd); 95 96 bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY; 97 samsungq10_bl_set_intensity(bd); 98 99 return 0; 100 } 101 102 static int __devexit samsungq10_remove(struct platform_device *pdev) 103 { 104 105 struct backlight_device *bd = platform_get_drvdata(pdev); 106 107 bd->props.brightness = SAMSUNGQ10_BL_DEFAULT_INTENSITY; 108 samsungq10_bl_set_intensity(bd); 109 110 backlight_device_unregister(bd); 111 112 return 0; 113 } 114 115 static struct platform_driver samsungq10_driver = { 116 .driver = { 117 .name = KBUILD_MODNAME, 118 .owner = THIS_MODULE, 119 .pm = &samsungq10_pm_ops, 120 }, 121 .probe = samsungq10_probe, 122 .remove = __devexit_p(samsungq10_remove), 123 }; 124 125 static struct platform_device *samsungq10_device; 126 127 static int __init dmi_check_callback(const struct dmi_system_id *id) 128 { 129 printk(KERN_INFO KBUILD_MODNAME ": found model '%s'\n", id->ident); 130 return 1; 131 } 132 133 static struct dmi_system_id __initdata samsungq10_dmi_table[] = { 134 { 135 .ident = "Samsung Q10", 136 .matches = { 137 DMI_MATCH(DMI_SYS_VENDOR, "Samsung"), 138 DMI_MATCH(DMI_PRODUCT_NAME, "SQ10"), 139 }, 140 .callback = dmi_check_callback, 141 }, 142 { 143 .ident = "Samsung Q20", 144 .matches = { 145 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics"), 146 DMI_MATCH(DMI_PRODUCT_NAME, "SENS Q20"), 147 }, 148 .callback = dmi_check_callback, 149 }, 150 { 151 .ident = "Samsung Q25", 152 .matches = { 153 DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG Electronics"), 154 DMI_MATCH(DMI_PRODUCT_NAME, "NQ25"), 155 }, 156 .callback = dmi_check_callback, 157 }, 158 { 159 .ident = "Dell Latitude X200", 160 .matches = { 161 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"), 162 DMI_MATCH(DMI_PRODUCT_NAME, "X200"), 163 }, 164 .callback = dmi_check_callback, 165 }, 166 { }, 167 }; 168 MODULE_DEVICE_TABLE(dmi, samsungq10_dmi_table); 169 170 static int __init samsungq10_init(void) 171 { 172 if (!force && !dmi_check_system(samsungq10_dmi_table)) 173 return -ENODEV; 174 175 samsungq10_device = platform_create_bundle(&samsungq10_driver, 176 samsungq10_probe, 177 NULL, 0, NULL, 0); 178 179 if (IS_ERR(samsungq10_device)) 180 return PTR_ERR(samsungq10_device); 181 182 return 0; 183 } 184 185 static void __exit samsungq10_exit(void) 186 { 187 platform_device_unregister(samsungq10_device); 188 platform_driver_unregister(&samsungq10_driver); 189 } 190 191 module_init(samsungq10_init); 192 module_exit(samsungq10_exit); 193 194 MODULE_AUTHOR("Frederick van der Wyck <fvanderwyck@gmail.com>"); 195 MODULE_DESCRIPTION("Samsung Q10 Driver"); 196 MODULE_LICENSE("GPL"); 197