Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.18-rc5 366 lines 9.3 kB view raw
1/* 2 * Copyright (C) STMicroelectronics SA 2014 3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 4 * Fabien Dessenne <fabien.dessenne@st.com> 5 * Vincent Abriou <vincent.abriou@st.com> 6 * for STMicroelectronics. 7 * License terms: GNU General Public License (GPL), version 2 8 */ 9 10#include <linux/module.h> 11#include <linux/notifier.h> 12#include <linux/platform_device.h> 13 14#include <drm/drmP.h> 15 16#include "sti_vtg.h" 17 18#define VTG_TYPE_MASTER 0 19#define VTG_TYPE_SLAVE_BY_EXT0 1 20 21/* registers offset */ 22#define VTG_MODE 0x0000 23#define VTG_CLKLN 0x0008 24#define VTG_HLFLN 0x000C 25#define VTG_DRST_AUTOC 0x0010 26#define VTG_VID_TFO 0x0040 27#define VTG_VID_TFS 0x0044 28#define VTG_VID_BFO 0x0048 29#define VTG_VID_BFS 0x004C 30 31#define VTG_HOST_ITS 0x0078 32#define VTG_HOST_ITS_BCLR 0x007C 33#define VTG_HOST_ITM_BCLR 0x0088 34#define VTG_HOST_ITM_BSET 0x008C 35 36#define VTG_H_HD_1 0x00C0 37#define VTG_TOP_V_VD_1 0x00C4 38#define VTG_BOT_V_VD_1 0x00C8 39#define VTG_TOP_V_HD_1 0x00CC 40#define VTG_BOT_V_HD_1 0x00D0 41 42#define VTG_H_HD_2 0x00E0 43#define VTG_TOP_V_VD_2 0x00E4 44#define VTG_BOT_V_VD_2 0x00E8 45#define VTG_TOP_V_HD_2 0x00EC 46#define VTG_BOT_V_HD_2 0x00F0 47 48#define VTG_H_HD_3 0x0100 49#define VTG_TOP_V_VD_3 0x0104 50#define VTG_BOT_V_VD_3 0x0108 51#define VTG_TOP_V_HD_3 0x010C 52#define VTG_BOT_V_HD_3 0x0110 53 54#define VTG_IRQ_BOTTOM BIT(0) 55#define VTG_IRQ_TOP BIT(1) 56#define VTG_IRQ_MASK (VTG_IRQ_TOP | VTG_IRQ_BOTTOM) 57 58/* delay introduced by the Arbitrary Waveform Generator in nb of pixels */ 59#define AWG_DELAY_HD (-9) 60#define AWG_DELAY_ED (-8) 61#define AWG_DELAY_SD (-7) 62 63LIST_HEAD(vtg_lookup); 64 65/** 66 * STI VTG structure 67 * 68 * @dev: pointer to device driver 69 * @data: data associated to the device 70 * @irq: VTG irq 71 * @type: VTG type (main or aux) 72 * @notifier_list: notifier callback 73 * @crtc_id: the crtc id for vblank event 74 * @slave: slave vtg 75 * @link: List node to link the structure in lookup list 76 */ 77struct sti_vtg { 78 struct device *dev; 79 struct device_node *np; 80 void __iomem *regs; 81 int irq; 82 u32 irq_status; 83 struct raw_notifier_head notifier_list; 84 int crtc_id; 85 struct sti_vtg *slave; 86 struct list_head link; 87}; 88 89static void vtg_register(struct sti_vtg *vtg) 90{ 91 list_add_tail(&vtg->link, &vtg_lookup); 92} 93 94struct sti_vtg *of_vtg_find(struct device_node *np) 95{ 96 struct sti_vtg *vtg; 97 98 list_for_each_entry(vtg, &vtg_lookup, link) { 99 if (vtg->np == np) 100 return vtg; 101 } 102 return NULL; 103} 104EXPORT_SYMBOL(of_vtg_find); 105 106static void vtg_reset(struct sti_vtg *vtg) 107{ 108 /* reset slave and then master */ 109 if (vtg->slave) 110 vtg_reset(vtg->slave); 111 112 writel(1, vtg->regs + VTG_DRST_AUTOC); 113} 114 115static void vtg_set_mode(struct sti_vtg *vtg, 116 int type, const struct drm_display_mode *mode) 117{ 118 u32 tmp; 119 120 if (vtg->slave) 121 vtg_set_mode(vtg->slave, VTG_TYPE_SLAVE_BY_EXT0, mode); 122 123 writel(mode->htotal, vtg->regs + VTG_CLKLN); 124 writel(mode->vtotal * 2, vtg->regs + VTG_HLFLN); 125 126 tmp = (mode->vtotal - mode->vsync_start + 1) << 16; 127 tmp |= mode->htotal - mode->hsync_start; 128 writel(tmp, vtg->regs + VTG_VID_TFO); 129 writel(tmp, vtg->regs + VTG_VID_BFO); 130 131 tmp = (mode->vdisplay + mode->vtotal - mode->vsync_start + 1) << 16; 132 tmp |= mode->hdisplay + mode->htotal - mode->hsync_start; 133 writel(tmp, vtg->regs + VTG_VID_TFS); 134 writel(tmp, vtg->regs + VTG_VID_BFS); 135 136 /* prepare VTG set 1 and 2 for HDMI and VTG set 3 for HD DAC */ 137 tmp = (mode->hsync_end - mode->hsync_start) << 16; 138 writel(tmp, vtg->regs + VTG_H_HD_1); 139 writel(tmp, vtg->regs + VTG_H_HD_2); 140 141 tmp = (mode->vsync_end - mode->vsync_start + 1) << 16; 142 tmp |= 1; 143 writel(tmp, vtg->regs + VTG_TOP_V_VD_1); 144 writel(tmp, vtg->regs + VTG_BOT_V_VD_1); 145 writel(0, vtg->regs + VTG_TOP_V_HD_1); 146 writel(0, vtg->regs + VTG_BOT_V_HD_1); 147 148 /* prepare VTG set 2 for for HD DCS */ 149 writel(tmp, vtg->regs + VTG_TOP_V_VD_2); 150 writel(tmp, vtg->regs + VTG_BOT_V_VD_2); 151 writel(0, vtg->regs + VTG_TOP_V_HD_2); 152 writel(0, vtg->regs + VTG_BOT_V_HD_2); 153 154 /* prepare VTG set 3 for HD Analog in HD mode */ 155 tmp = (mode->hsync_end - mode->hsync_start + AWG_DELAY_HD) << 16; 156 tmp |= mode->htotal + AWG_DELAY_HD; 157 writel(tmp, vtg->regs + VTG_H_HD_3); 158 159 tmp = (mode->vsync_end - mode->vsync_start) << 16; 160 tmp |= mode->vtotal; 161 writel(tmp, vtg->regs + VTG_TOP_V_VD_3); 162 writel(tmp, vtg->regs + VTG_BOT_V_VD_3); 163 164 tmp = (mode->htotal + AWG_DELAY_HD) << 16; 165 tmp |= mode->htotal + AWG_DELAY_HD; 166 writel(tmp, vtg->regs + VTG_TOP_V_HD_3); 167 writel(tmp, vtg->regs + VTG_BOT_V_HD_3); 168 169 /* mode */ 170 writel(type, vtg->regs + VTG_MODE); 171} 172 173static void vtg_enable_irq(struct sti_vtg *vtg) 174{ 175 /* clear interrupt status and mask */ 176 writel(0xFFFF, vtg->regs + VTG_HOST_ITS_BCLR); 177 writel(0xFFFF, vtg->regs + VTG_HOST_ITM_BCLR); 178 writel(VTG_IRQ_MASK, vtg->regs + VTG_HOST_ITM_BSET); 179} 180 181void sti_vtg_set_config(struct sti_vtg *vtg, 182 const struct drm_display_mode *mode) 183{ 184 /* write configuration */ 185 vtg_set_mode(vtg, VTG_TYPE_MASTER, mode); 186 187 vtg_reset(vtg); 188 189 /* enable irq for the vtg vblank synchro */ 190 if (vtg->slave) 191 vtg_enable_irq(vtg->slave); 192 else 193 vtg_enable_irq(vtg); 194} 195EXPORT_SYMBOL(sti_vtg_set_config); 196 197/** 198 * sti_vtg_get_line_number 199 * 200 * @mode: display mode to be used 201 * @y: line 202 * 203 * Return the line number according to the display mode taking 204 * into account the Sync and Back Porch information. 205 * Video frame line numbers start at 1, y starts at 0. 206 * In interlaced modes the start line is the field line number of the odd 207 * field, but y is still defined as a progressive frame. 208 */ 209u32 sti_vtg_get_line_number(struct drm_display_mode mode, int y) 210{ 211 u32 start_line = mode.vtotal - mode.vsync_start + 1; 212 213 if (mode.flags & DRM_MODE_FLAG_INTERLACE) 214 start_line *= 2; 215 216 return start_line + y; 217} 218EXPORT_SYMBOL(sti_vtg_get_line_number); 219 220/** 221 * sti_vtg_get_pixel_number 222 * 223 * @mode: display mode to be used 224 * @x: row 225 * 226 * Return the pixel number according to the display mode taking 227 * into account the Sync and Back Porch information. 228 * Pixels are counted from 0. 229 */ 230u32 sti_vtg_get_pixel_number(struct drm_display_mode mode, int x) 231{ 232 return mode.htotal - mode.hsync_start + x; 233} 234EXPORT_SYMBOL(sti_vtg_get_pixel_number); 235 236int sti_vtg_register_client(struct sti_vtg *vtg, 237 struct notifier_block *nb, int crtc_id) 238{ 239 if (vtg->slave) 240 return sti_vtg_register_client(vtg->slave, nb, crtc_id); 241 242 vtg->crtc_id = crtc_id; 243 return raw_notifier_chain_register(&vtg->notifier_list, nb); 244} 245EXPORT_SYMBOL(sti_vtg_register_client); 246 247int sti_vtg_unregister_client(struct sti_vtg *vtg, struct notifier_block *nb) 248{ 249 if (vtg->slave) 250 return sti_vtg_unregister_client(vtg->slave, nb); 251 252 return raw_notifier_chain_unregister(&vtg->notifier_list, nb); 253} 254EXPORT_SYMBOL(sti_vtg_unregister_client); 255 256static irqreturn_t vtg_irq_thread(int irq, void *arg) 257{ 258 struct sti_vtg *vtg = arg; 259 u32 event; 260 261 event = (vtg->irq_status & VTG_IRQ_TOP) ? 262 VTG_TOP_FIELD_EVENT : VTG_BOTTOM_FIELD_EVENT; 263 264 raw_notifier_call_chain(&vtg->notifier_list, event, &vtg->crtc_id); 265 266 return IRQ_HANDLED; 267} 268 269static irqreturn_t vtg_irq(int irq, void *arg) 270{ 271 struct sti_vtg *vtg = arg; 272 273 vtg->irq_status = readl(vtg->regs + VTG_HOST_ITS); 274 275 writel(vtg->irq_status, vtg->regs + VTG_HOST_ITS_BCLR); 276 277 /* force sync bus write */ 278 readl(vtg->regs + VTG_HOST_ITS); 279 280 return IRQ_WAKE_THREAD; 281} 282 283static int vtg_probe(struct platform_device *pdev) 284{ 285 struct device *dev = &pdev->dev; 286 struct device_node *np; 287 struct sti_vtg *vtg; 288 struct resource *res; 289 char irq_name[32]; 290 int ret; 291 292 vtg = devm_kzalloc(dev, sizeof(*vtg), GFP_KERNEL); 293 if (!vtg) 294 return -ENOMEM; 295 296 vtg->dev = dev; 297 vtg->np = pdev->dev.of_node; 298 299 /* Get Memory ressources */ 300 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 301 if (!res) { 302 DRM_ERROR("Get memory resource failed\n"); 303 return -ENOMEM; 304 } 305 vtg->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); 306 307 np = of_parse_phandle(pdev->dev.of_node, "st,slave", 0); 308 if (np) { 309 vtg->slave = of_vtg_find(np); 310 311 if (!vtg->slave) 312 return -EPROBE_DEFER; 313 } else { 314 vtg->irq = platform_get_irq(pdev, 0); 315 if (IS_ERR_VALUE(vtg->irq)) { 316 DRM_ERROR("Failed to get VTG interrupt\n"); 317 return vtg->irq; 318 } 319 320 snprintf(irq_name, sizeof(irq_name), "vsync-%s", 321 dev_name(vtg->dev)); 322 323 RAW_INIT_NOTIFIER_HEAD(&vtg->notifier_list); 324 325 ret = devm_request_threaded_irq(dev, vtg->irq, vtg_irq, 326 vtg_irq_thread, IRQF_ONESHOT, irq_name, vtg); 327 if (IS_ERR_VALUE(ret)) { 328 DRM_ERROR("Failed to register VTG interrupt\n"); 329 return ret; 330 } 331 } 332 333 vtg_register(vtg); 334 platform_set_drvdata(pdev, vtg); 335 336 DRM_INFO("%s %s\n", __func__, dev_name(vtg->dev)); 337 338 return 0; 339} 340 341static int vtg_remove(struct platform_device *pdev) 342{ 343 return 0; 344} 345 346static const struct of_device_id vtg_of_match[] = { 347 { .compatible = "st,vtg", }, 348 { /* sentinel */ } 349}; 350MODULE_DEVICE_TABLE(of, vtg_of_match); 351 352struct platform_driver sti_vtg_driver = { 353 .driver = { 354 .name = "sti-vtg", 355 .owner = THIS_MODULE, 356 .of_match_table = vtg_of_match, 357 }, 358 .probe = vtg_probe, 359 .remove = vtg_remove, 360}; 361 362module_platform_driver(sti_vtg_driver); 363 364MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 365MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 366MODULE_LICENSE("GPL");