0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029 #include <linux/bitops.h>
0030 #include <linux/init.h>
0031 #include <linux/kernel.h>
0032 #include <linux/mfd/syscon.h>
0033 #include <linux/mod_devicetable.h>
0034 #include <linux/module.h>
0035 #include <linux/platform_device.h>
0036 #include <linux/regmap.h>
0037
0038 #include <video/of_videomode.h>
0039 #include <video/videomode.h>
0040
0041 #include <drm/drm_modes.h>
0042 #include <drm/drm_panel.h>
0043
0044
0045
0046
0047
0048
0049 #define SYS_CLCD 0x50
0050
0051
0052 #define SYS_CLCD_CLCDID_MASK (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12))
0053 #define SYS_CLCD_ID_SANYO_3_8 (0x00 << 8)
0054 #define SYS_CLCD_ID_SHARP_8_4 (0x01 << 8)
0055 #define SYS_CLCD_ID_EPSON_2_2 (0x02 << 8)
0056 #define SYS_CLCD_ID_SANYO_2_5 (0x07 << 8)
0057 #define SYS_CLCD_ID_VGA (0x1f << 8)
0058
0059
0060 #define IB2_CTRL 0x00
0061 #define IB2_CTRL_LCD_SD BIT(1)
0062 #define IB2_CTRL_LCD_BL_ON BIT(0)
0063 #define IB2_CTRL_LCD_MASK (BIT(0)|BIT(1))
0064
0065
0066
0067
0068 struct versatile_panel_type {
0069
0070
0071
0072 const char *name;
0073
0074
0075
0076 u32 magic;
0077
0078
0079
0080 struct drm_display_mode mode;
0081
0082
0083
0084 u32 bus_flags;
0085
0086
0087
0088 u32 width_mm;
0089
0090
0091
0092 u32 height_mm;
0093
0094
0095
0096 bool ib2;
0097 };
0098
0099
0100
0101
0102 struct versatile_panel {
0103
0104
0105
0106 struct device *dev;
0107
0108
0109
0110 struct drm_panel panel;
0111
0112
0113
0114 const struct versatile_panel_type *panel_type;
0115
0116
0117
0118 struct regmap *map;
0119
0120
0121
0122 struct regmap *ib2_map;
0123 };
0124
0125 static const struct versatile_panel_type versatile_panels[] = {
0126
0127
0128
0129
0130
0131 {
0132 .name = "Sanyo TM38QV67A02A",
0133 .magic = SYS_CLCD_ID_SANYO_3_8,
0134 .width_mm = 79,
0135 .height_mm = 54,
0136 .mode = {
0137 .clock = 10000,
0138 .hdisplay = 320,
0139 .hsync_start = 320 + 6,
0140 .hsync_end = 320 + 6 + 6,
0141 .htotal = 320 + 6 + 6 + 6,
0142 .vdisplay = 240,
0143 .vsync_start = 240 + 5,
0144 .vsync_end = 240 + 5 + 6,
0145 .vtotal = 240 + 5 + 6 + 5,
0146 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
0147 },
0148 },
0149
0150
0151
0152
0153
0154 {
0155 .name = "Sharp LQ084V1DG21",
0156 .magic = SYS_CLCD_ID_SHARP_8_4,
0157 .width_mm = 171,
0158 .height_mm = 130,
0159 .mode = {
0160 .clock = 25000,
0161 .hdisplay = 640,
0162 .hsync_start = 640 + 24,
0163 .hsync_end = 640 + 24 + 96,
0164 .htotal = 640 + 24 + 96 + 24,
0165 .vdisplay = 480,
0166 .vsync_start = 480 + 11,
0167 .vsync_end = 480 + 11 + 2,
0168 .vtotal = 480 + 11 + 2 + 32,
0169 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
0170 },
0171 },
0172
0173
0174
0175
0176 {
0177 .name = "Epson L2F50113T00",
0178 .magic = SYS_CLCD_ID_EPSON_2_2,
0179 .width_mm = 34,
0180 .height_mm = 45,
0181 .mode = {
0182 .clock = 62500,
0183 .hdisplay = 176,
0184 .hsync_start = 176 + 2,
0185 .hsync_end = 176 + 2 + 3,
0186 .htotal = 176 + 2 + 3 + 3,
0187 .vdisplay = 220,
0188 .vsync_start = 220 + 0,
0189 .vsync_end = 220 + 0 + 2,
0190 .vtotal = 220 + 0 + 2 + 1,
0191 .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC,
0192 },
0193 .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
0194 },
0195
0196
0197
0198
0199 {
0200 .name = "Sanyo ALR252RGT",
0201 .magic = SYS_CLCD_ID_SANYO_2_5,
0202 .width_mm = 37,
0203 .height_mm = 50,
0204 .mode = {
0205 .clock = 5400,
0206 .hdisplay = 240,
0207 .hsync_start = 240 + 10,
0208 .hsync_end = 240 + 10 + 10,
0209 .htotal = 240 + 10 + 10 + 20,
0210 .vdisplay = 320,
0211 .vsync_start = 320 + 2,
0212 .vsync_end = 320 + 2 + 2,
0213 .vtotal = 320 + 2 + 2 + 2,
0214 .flags = DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC,
0215 },
0216 .bus_flags = DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE,
0217 .ib2 = true,
0218 },
0219 };
0220
0221 static inline struct versatile_panel *
0222 to_versatile_panel(struct drm_panel *panel)
0223 {
0224 return container_of(panel, struct versatile_panel, panel);
0225 }
0226
0227 static int versatile_panel_disable(struct drm_panel *panel)
0228 {
0229 struct versatile_panel *vpanel = to_versatile_panel(panel);
0230
0231
0232 if (vpanel->ib2_map) {
0233 dev_dbg(vpanel->dev, "disable IB2 display\n");
0234 regmap_update_bits(vpanel->ib2_map,
0235 IB2_CTRL,
0236 IB2_CTRL_LCD_MASK,
0237 IB2_CTRL_LCD_SD);
0238 }
0239
0240 return 0;
0241 }
0242
0243 static int versatile_panel_enable(struct drm_panel *panel)
0244 {
0245 struct versatile_panel *vpanel = to_versatile_panel(panel);
0246
0247
0248 if (vpanel->ib2_map) {
0249 dev_dbg(vpanel->dev, "enable IB2 display\n");
0250 regmap_update_bits(vpanel->ib2_map,
0251 IB2_CTRL,
0252 IB2_CTRL_LCD_MASK,
0253 IB2_CTRL_LCD_BL_ON);
0254 }
0255
0256 return 0;
0257 }
0258
0259 static int versatile_panel_get_modes(struct drm_panel *panel,
0260 struct drm_connector *connector)
0261 {
0262 struct versatile_panel *vpanel = to_versatile_panel(panel);
0263 struct drm_display_mode *mode;
0264
0265 connector->display_info.width_mm = vpanel->panel_type->width_mm;
0266 connector->display_info.height_mm = vpanel->panel_type->height_mm;
0267 connector->display_info.bus_flags = vpanel->panel_type->bus_flags;
0268
0269 mode = drm_mode_duplicate(connector->dev, &vpanel->panel_type->mode);
0270 drm_mode_set_name(mode);
0271 mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
0272
0273 mode->width_mm = vpanel->panel_type->width_mm;
0274 mode->height_mm = vpanel->panel_type->height_mm;
0275 drm_mode_probed_add(connector, mode);
0276
0277 return 1;
0278 }
0279
0280 static const struct drm_panel_funcs versatile_panel_drm_funcs = {
0281 .disable = versatile_panel_disable,
0282 .enable = versatile_panel_enable,
0283 .get_modes = versatile_panel_get_modes,
0284 };
0285
0286 static int versatile_panel_probe(struct platform_device *pdev)
0287 {
0288 struct device *dev = &pdev->dev;
0289 struct versatile_panel *vpanel;
0290 struct device *parent;
0291 struct regmap *map;
0292 int ret;
0293 u32 val;
0294 int i;
0295
0296 parent = dev->parent;
0297 if (!parent) {
0298 dev_err(dev, "no parent for versatile panel\n");
0299 return -ENODEV;
0300 }
0301 map = syscon_node_to_regmap(parent->of_node);
0302 if (IS_ERR(map)) {
0303 dev_err(dev, "no regmap for versatile panel parent\n");
0304 return PTR_ERR(map);
0305 }
0306
0307 vpanel = devm_kzalloc(dev, sizeof(*vpanel), GFP_KERNEL);
0308 if (!vpanel)
0309 return -ENOMEM;
0310
0311 ret = regmap_read(map, SYS_CLCD, &val);
0312 if (ret) {
0313 dev_err(dev, "cannot access syscon regs\n");
0314 return ret;
0315 }
0316
0317 val &= SYS_CLCD_CLCDID_MASK;
0318
0319 for (i = 0; i < ARRAY_SIZE(versatile_panels); i++) {
0320 const struct versatile_panel_type *pt;
0321
0322 pt = &versatile_panels[i];
0323 if (pt->magic == val) {
0324 vpanel->panel_type = pt;
0325 break;
0326 }
0327 }
0328
0329
0330 if (i == ARRAY_SIZE(versatile_panels)) {
0331 dev_info(dev, "no panel detected\n");
0332 return -ENODEV;
0333 }
0334
0335 dev_info(dev, "detected: %s\n", vpanel->panel_type->name);
0336 vpanel->dev = dev;
0337 vpanel->map = map;
0338
0339
0340 if (vpanel->panel_type->ib2) {
0341 vpanel->ib2_map = syscon_regmap_lookup_by_compatible(
0342 "arm,versatile-ib2-syscon");
0343 if (IS_ERR(vpanel->ib2_map))
0344 vpanel->ib2_map = NULL;
0345 else
0346 dev_info(dev, "panel mounted on IB2 daughterboard\n");
0347 }
0348
0349 drm_panel_init(&vpanel->panel, dev, &versatile_panel_drm_funcs,
0350 DRM_MODE_CONNECTOR_DPI);
0351
0352 drm_panel_add(&vpanel->panel);
0353
0354 return 0;
0355 }
0356
0357 static const struct of_device_id versatile_panel_match[] = {
0358 { .compatible = "arm,versatile-tft-panel", },
0359 {},
0360 };
0361 MODULE_DEVICE_TABLE(of, versatile_panel_match);
0362
0363 static struct platform_driver versatile_panel_driver = {
0364 .probe = versatile_panel_probe,
0365 .driver = {
0366 .name = "versatile-tft-panel",
0367 .of_match_table = versatile_panel_match,
0368 },
0369 };
0370 module_platform_driver(versatile_panel_driver);
0371
0372 MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
0373 MODULE_DESCRIPTION("ARM Versatile panel driver");
0374 MODULE_LICENSE("GPL v2");