1/* 2 * Copyright (C) STMicroelectronics SA 2014 3 * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 4 * Vincent Abriou <vincent.abriou@st.com> 5 * for STMicroelectronics. 6 * License terms: GNU General Public License (GPL), version 2 7 */ 8 9#include <linux/clk.h> 10#include <linux/component.h> 11#include <linux/module.h> 12#include <linux/of_platform.h> 13#include <linux/platform_device.h> 14#include <linux/reset.h> 15 16#include <drm/drmP.h> 17#include <drm/drm_crtc_helper.h> 18 19#include "sti_drm_crtc.h" 20 21/* glue registers */ 22#define TVO_CSC_MAIN_M0 0x000 23#define TVO_CSC_MAIN_M1 0x004 24#define TVO_CSC_MAIN_M2 0x008 25#define TVO_CSC_MAIN_M3 0x00c 26#define TVO_CSC_MAIN_M4 0x010 27#define TVO_CSC_MAIN_M5 0x014 28#define TVO_CSC_MAIN_M6 0x018 29#define TVO_CSC_MAIN_M7 0x01c 30#define TVO_MAIN_IN_VID_FORMAT 0x030 31#define TVO_CSC_AUX_M0 0x100 32#define TVO_CSC_AUX_M1 0x104 33#define TVO_CSC_AUX_M2 0x108 34#define TVO_CSC_AUX_M3 0x10c 35#define TVO_CSC_AUX_M4 0x110 36#define TVO_CSC_AUX_M5 0x114 37#define TVO_CSC_AUX_M6 0x118 38#define TVO_CSC_AUX_M7 0x11c 39#define TVO_AUX_IN_VID_FORMAT 0x130 40#define TVO_VIP_HDF 0x400 41#define TVO_HD_SYNC_SEL 0x418 42#define TVO_HD_DAC_CFG_OFF 0x420 43#define TVO_VIP_HDMI 0x500 44#define TVO_HDMI_FORCE_COLOR_0 0x504 45#define TVO_HDMI_FORCE_COLOR_1 0x508 46#define TVO_HDMI_CLIP_VALUE_B_CB 0x50c 47#define TVO_HDMI_CLIP_VALUE_Y_G 0x510 48#define TVO_HDMI_CLIP_VALUE_R_CR 0x514 49#define TVO_HDMI_SYNC_SEL 0x518 50#define TVO_HDMI_DFV_OBS 0x540 51#define TVO_VIP_DVO 0x600 52#define TVO_DVO_SYNC_SEL 0x618 53#define TVO_DVO_CONFIG 0x620 54 55#define TVO_IN_FMT_SIGNED BIT(0) 56#define TVO_SYNC_EXT BIT(4) 57 58#define TVO_VIP_REORDER_R_SHIFT 24 59#define TVO_VIP_REORDER_G_SHIFT 20 60#define TVO_VIP_REORDER_B_SHIFT 16 61#define TVO_VIP_REORDER_MASK 0x3 62#define TVO_VIP_REORDER_Y_G_SEL 0 63#define TVO_VIP_REORDER_CB_B_SEL 1 64#define TVO_VIP_REORDER_CR_R_SEL 2 65 66#define TVO_VIP_CLIP_SHIFT 8 67#define TVO_VIP_CLIP_MASK 0x7 68#define TVO_VIP_CLIP_DISABLED 0 69#define TVO_VIP_CLIP_EAV_SAV 1 70#define TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y 2 71#define TVO_VIP_CLIP_LIMITED_RANGE_CB_CR 3 72#define TVO_VIP_CLIP_PROG_RANGE 4 73 74#define TVO_VIP_RND_SHIFT 4 75#define TVO_VIP_RND_MASK 0x3 76#define TVO_VIP_RND_8BIT_ROUNDED 0 77#define TVO_VIP_RND_10BIT_ROUNDED 1 78#define TVO_VIP_RND_12BIT_ROUNDED 2 79 80#define TVO_VIP_SEL_INPUT_MASK 0xf 81#define TVO_VIP_SEL_INPUT_MAIN 0x0 82#define TVO_VIP_SEL_INPUT_AUX 0x8 83#define TVO_VIP_SEL_INPUT_FORCE_COLOR 0xf 84#define TVO_VIP_SEL_INPUT_BYPASS_MASK 0x1 85#define TVO_VIP_SEL_INPUT_BYPASSED 1 86 87#define TVO_SYNC_MAIN_VTG_SET_REF 0x00 88#define TVO_SYNC_MAIN_VTG_SET_1 0x01 89#define TVO_SYNC_MAIN_VTG_SET_2 0x02 90#define TVO_SYNC_MAIN_VTG_SET_3 0x03 91#define TVO_SYNC_MAIN_VTG_SET_4 0x04 92#define TVO_SYNC_MAIN_VTG_SET_5 0x05 93#define TVO_SYNC_MAIN_VTG_SET_6 0x06 94#define TVO_SYNC_AUX_VTG_SET_REF 0x10 95#define TVO_SYNC_AUX_VTG_SET_1 0x11 96#define TVO_SYNC_AUX_VTG_SET_2 0x12 97#define TVO_SYNC_AUX_VTG_SET_3 0x13 98#define TVO_SYNC_AUX_VTG_SET_4 0x14 99#define TVO_SYNC_AUX_VTG_SET_5 0x15 100#define TVO_SYNC_AUX_VTG_SET_6 0x16 101 102#define TVO_SYNC_HD_DCS_SHIFT 8 103 104#define TVO_SYNC_DVO_PAD_HSYNC_SHIFT 8 105#define TVO_SYNC_DVO_PAD_VSYNC_SHIFT 16 106 107#define ENCODER_CRTC_MASK (BIT(0) | BIT(1)) 108 109/* enum listing the supported output data format */ 110enum sti_tvout_video_out_type { 111 STI_TVOUT_VIDEO_OUT_RGB, 112 STI_TVOUT_VIDEO_OUT_YUV, 113}; 114 115struct sti_tvout { 116 struct device *dev; 117 struct drm_device *drm_dev; 118 void __iomem *regs; 119 struct reset_control *reset; 120 struct drm_encoder *hdmi; 121 struct drm_encoder *hda; 122 struct drm_encoder *dvo; 123}; 124 125struct sti_tvout_encoder { 126 struct drm_encoder encoder; 127 struct sti_tvout *tvout; 128}; 129 130#define to_sti_tvout_encoder(x) \ 131 container_of(x, struct sti_tvout_encoder, encoder) 132 133#define to_sti_tvout(x) to_sti_tvout_encoder(x)->tvout 134 135/* preformatter conversion matrix */ 136static const u32 rgb_to_ycbcr_601[8] = { 137 0xF927082E, 0x04C9FEAB, 0x01D30964, 0xFA95FD3D, 138 0x0000082E, 0x00002000, 0x00002000, 0x00000000 139}; 140 141/* 709 RGB to YCbCr */ 142static const u32 rgb_to_ycbcr_709[8] = { 143 0xF891082F, 0x0367FF40, 0x01280B71, 0xF9B1FE20, 144 0x0000082F, 0x00002000, 0x00002000, 0x00000000 145}; 146 147static u32 tvout_read(struct sti_tvout *tvout, int offset) 148{ 149 return readl(tvout->regs + offset); 150} 151 152static void tvout_write(struct sti_tvout *tvout, u32 val, int offset) 153{ 154 writel(val, tvout->regs + offset); 155} 156 157/** 158 * Set the clipping mode of a VIP 159 * 160 * @tvout: tvout structure 161 * @reg: register to set 162 * @cr_r: 163 * @y_g: 164 * @cb_b: 165 */ 166static void tvout_vip_set_color_order(struct sti_tvout *tvout, int reg, 167 u32 cr_r, u32 y_g, u32 cb_b) 168{ 169 u32 val = tvout_read(tvout, reg); 170 171 val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_R_SHIFT); 172 val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_G_SHIFT); 173 val &= ~(TVO_VIP_REORDER_MASK << TVO_VIP_REORDER_B_SHIFT); 174 val |= cr_r << TVO_VIP_REORDER_R_SHIFT; 175 val |= y_g << TVO_VIP_REORDER_G_SHIFT; 176 val |= cb_b << TVO_VIP_REORDER_B_SHIFT; 177 178 tvout_write(tvout, val, reg); 179} 180 181/** 182 * Set the clipping mode of a VIP 183 * 184 * @tvout: tvout structure 185 * @reg: register to set 186 * @range: clipping range 187 */ 188static void tvout_vip_set_clip_mode(struct sti_tvout *tvout, int reg, u32 range) 189{ 190 u32 val = tvout_read(tvout, reg); 191 192 val &= ~(TVO_VIP_CLIP_MASK << TVO_VIP_CLIP_SHIFT); 193 val |= range << TVO_VIP_CLIP_SHIFT; 194 tvout_write(tvout, val, reg); 195} 196 197/** 198 * Set the rounded value of a VIP 199 * 200 * @tvout: tvout structure 201 * @reg: register to set 202 * @rnd: rounded val per component 203 */ 204static void tvout_vip_set_rnd(struct sti_tvout *tvout, int reg, u32 rnd) 205{ 206 u32 val = tvout_read(tvout, reg); 207 208 val &= ~(TVO_VIP_RND_MASK << TVO_VIP_RND_SHIFT); 209 val |= rnd << TVO_VIP_RND_SHIFT; 210 tvout_write(tvout, val, reg); 211} 212 213/** 214 * Select the VIP input 215 * 216 * @tvout: tvout structure 217 * @reg: register to set 218 * @main_path: main or auxiliary path 219 * @sel_input_logic_inverted: need to invert the logic 220 * @sel_input: selected_input (main/aux + conv) 221 */ 222static void tvout_vip_set_sel_input(struct sti_tvout *tvout, 223 int reg, 224 bool main_path, 225 bool sel_input_logic_inverted, 226 enum sti_tvout_video_out_type video_out) 227{ 228 u32 sel_input; 229 u32 val = tvout_read(tvout, reg); 230 231 if (main_path) 232 sel_input = TVO_VIP_SEL_INPUT_MAIN; 233 else 234 sel_input = TVO_VIP_SEL_INPUT_AUX; 235 236 switch (video_out) { 237 case STI_TVOUT_VIDEO_OUT_RGB: 238 sel_input |= TVO_VIP_SEL_INPUT_BYPASSED; 239 break; 240 case STI_TVOUT_VIDEO_OUT_YUV: 241 sel_input &= ~TVO_VIP_SEL_INPUT_BYPASSED; 242 break; 243 } 244 245 /* on stih407 chip the sel_input bypass mode logic is inverted */ 246 if (sel_input_logic_inverted) 247 sel_input = sel_input ^ TVO_VIP_SEL_INPUT_BYPASS_MASK; 248 249 val &= ~TVO_VIP_SEL_INPUT_MASK; 250 val |= sel_input; 251 tvout_write(tvout, val, reg); 252} 253 254/** 255 * Select the input video signed or unsigned 256 * 257 * @tvout: tvout structure 258 * @reg: register to set 259 * @in_vid_signed: used video input format 260 */ 261static void tvout_vip_set_in_vid_fmt(struct sti_tvout *tvout, 262 int reg, u32 in_vid_fmt) 263{ 264 u32 val = tvout_read(tvout, reg); 265 266 val &= ~TVO_IN_FMT_SIGNED; 267 val |= in_vid_fmt; 268 tvout_write(tvout, val, reg); 269} 270 271/** 272 * Start VIP block for DVO output 273 * 274 * @tvout: pointer on tvout structure 275 * @main_path: true if main path has to be used in the vip configuration 276 * else aux path is used. 277 */ 278static void tvout_dvo_start(struct sti_tvout *tvout, bool main_path) 279{ 280 struct device_node *node = tvout->dev->of_node; 281 bool sel_input_logic_inverted = false; 282 u32 tvo_in_vid_format; 283 int val; 284 285 dev_dbg(tvout->dev, "%s\n", __func__); 286 287 if (main_path) { 288 DRM_DEBUG_DRIVER("main vip for DVO\n"); 289 /* Select the input sync for dvo = VTG set 4 */ 290 val = TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; 291 val |= TVO_SYNC_MAIN_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; 292 val |= TVO_SYNC_MAIN_VTG_SET_4; 293 tvout_write(tvout, val, TVO_DVO_SYNC_SEL); 294 tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; 295 } else { 296 DRM_DEBUG_DRIVER("aux vip for DVO\n"); 297 /* Select the input sync for dvo = VTG set 4 */ 298 val = TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_VSYNC_SHIFT; 299 val |= TVO_SYNC_AUX_VTG_SET_4 << TVO_SYNC_DVO_PAD_HSYNC_SHIFT; 300 val |= TVO_SYNC_AUX_VTG_SET_4; 301 tvout_write(tvout, val, TVO_DVO_SYNC_SEL); 302 tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; 303 } 304 305 /* Set color channel order */ 306 tvout_vip_set_color_order(tvout, TVO_VIP_DVO, 307 TVO_VIP_REORDER_CR_R_SEL, 308 TVO_VIP_REORDER_Y_G_SEL, 309 TVO_VIP_REORDER_CB_B_SEL); 310 311 /* Set clipping mode (Limited range RGB/Y) */ 312 tvout_vip_set_clip_mode(tvout, TVO_VIP_DVO, 313 TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); 314 315 /* Set round mode (rounded to 8-bit per component) */ 316 tvout_vip_set_rnd(tvout, TVO_VIP_DVO, TVO_VIP_RND_8BIT_ROUNDED); 317 318 if (of_device_is_compatible(node, "st,stih407-tvout")) { 319 /* Set input video format */ 320 tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, 321 TVO_IN_FMT_SIGNED); 322 sel_input_logic_inverted = true; 323 } 324 325 /* Input selection */ 326 tvout_vip_set_sel_input(tvout, TVO_VIP_DVO, main_path, 327 sel_input_logic_inverted, 328 STI_TVOUT_VIDEO_OUT_RGB); 329} 330 331/** 332 * Start VIP block for HDMI output 333 * 334 * @tvout: pointer on tvout structure 335 * @main_path: true if main path has to be used in the vip configuration 336 * else aux path is used. 337 */ 338static void tvout_hdmi_start(struct sti_tvout *tvout, bool main_path) 339{ 340 struct device_node *node = tvout->dev->of_node; 341 bool sel_input_logic_inverted = false; 342 u32 tvo_in_vid_format; 343 344 dev_dbg(tvout->dev, "%s\n", __func__); 345 346 if (main_path) { 347 DRM_DEBUG_DRIVER("main vip for hdmi\n"); 348 /* select the input sync for hdmi = VTG set 1 */ 349 tvout_write(tvout, TVO_SYNC_MAIN_VTG_SET_1, TVO_HDMI_SYNC_SEL); 350 tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; 351 } else { 352 DRM_DEBUG_DRIVER("aux vip for hdmi\n"); 353 /* select the input sync for hdmi = VTG set 1 */ 354 tvout_write(tvout, TVO_SYNC_AUX_VTG_SET_1, TVO_HDMI_SYNC_SEL); 355 tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; 356 } 357 358 /* set color channel order */ 359 tvout_vip_set_color_order(tvout, TVO_VIP_HDMI, 360 TVO_VIP_REORDER_CR_R_SEL, 361 TVO_VIP_REORDER_Y_G_SEL, 362 TVO_VIP_REORDER_CB_B_SEL); 363 364 /* set clipping mode (Limited range RGB/Y) */ 365 tvout_vip_set_clip_mode(tvout, TVO_VIP_HDMI, 366 TVO_VIP_CLIP_LIMITED_RANGE_RGB_Y); 367 368 /* set round mode (rounded to 8-bit per component) */ 369 tvout_vip_set_rnd(tvout, TVO_VIP_HDMI, TVO_VIP_RND_8BIT_ROUNDED); 370 371 if (of_device_is_compatible(node, "st,stih407-tvout")) { 372 /* set input video format */ 373 tvout_vip_set_in_vid_fmt(tvout, tvo_in_vid_format, 374 TVO_IN_FMT_SIGNED); 375 sel_input_logic_inverted = true; 376 } 377 378 /* input selection */ 379 tvout_vip_set_sel_input(tvout, TVO_VIP_HDMI, main_path, 380 sel_input_logic_inverted, STI_TVOUT_VIDEO_OUT_RGB); 381} 382 383/** 384 * Start HDF VIP and HD DAC 385 * 386 * @tvout: pointer on tvout structure 387 * @main_path: true if main path has to be used in the vip configuration 388 * else aux path is used. 389 */ 390static void tvout_hda_start(struct sti_tvout *tvout, bool main_path) 391{ 392 struct device_node *node = tvout->dev->of_node; 393 bool sel_input_logic_inverted = false; 394 u32 tvo_in_vid_format; 395 int val; 396 397 dev_dbg(tvout->dev, "%s\n", __func__); 398 399 if (main_path) { 400 val = TVO_SYNC_MAIN_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; 401 val |= TVO_SYNC_MAIN_VTG_SET_3; 402 tvout_write(tvout, val, TVO_HD_SYNC_SEL); 403 tvo_in_vid_format = TVO_MAIN_IN_VID_FORMAT; 404 } else { 405 val = TVO_SYNC_AUX_VTG_SET_2 << TVO_SYNC_HD_DCS_SHIFT; 406 val |= TVO_SYNC_AUX_VTG_SET_3; 407 tvout_write(tvout, val, TVO_HD_SYNC_SEL); 408 tvo_in_vid_format = TVO_AUX_IN_VID_FORMAT; 409 } 410 411 /* set color channel order */ 412 tvout_vip_set_color_order(tvout, TVO_VIP_HDF, 413 TVO_VIP_REORDER_CR_R_SEL, 414 TVO_VIP_REORDER_Y_G_SEL, 415 TVO_VIP_REORDER_CB_B_SEL); 416 417 /* set clipping mode (EAV/SAV clipping) */ 418 tvout_vip_set_clip_mode(tvout, TVO_VIP_HDF, TVO_VIP_CLIP_EAV_SAV); 419 420 /* set round mode (rounded to 10-bit per component) */ 421 tvout_vip_set_rnd(tvout, TVO_VIP_HDF, TVO_VIP_RND_10BIT_ROUNDED); 422 423 if (of_device_is_compatible(node, "st,stih407-tvout")) { 424 /* set input video format */ 425 tvout_vip_set_in_vid_fmt(tvout, 426 tvo_in_vid_format, TVO_IN_FMT_SIGNED); 427 sel_input_logic_inverted = true; 428 } 429 430 /* Input selection */ 431 tvout_vip_set_sel_input(tvout, TVO_VIP_HDF, main_path, 432 sel_input_logic_inverted, 433 STI_TVOUT_VIDEO_OUT_YUV); 434 435 /* power up HD DAC */ 436 tvout_write(tvout, 0, TVO_HD_DAC_CFG_OFF); 437} 438 439static void sti_tvout_encoder_dpms(struct drm_encoder *encoder, int mode) 440{ 441} 442 443static bool sti_tvout_encoder_mode_fixup(struct drm_encoder *encoder, 444 const struct drm_display_mode *mode, 445 struct drm_display_mode *adjusted_mode) 446{ 447 return true; 448} 449 450static void sti_tvout_encoder_mode_set(struct drm_encoder *encoder, 451 struct drm_display_mode *mode, 452 struct drm_display_mode *adjusted_mode) 453{ 454} 455 456static void sti_tvout_encoder_prepare(struct drm_encoder *encoder) 457{ 458} 459 460static void sti_tvout_encoder_destroy(struct drm_encoder *encoder) 461{ 462 struct sti_tvout_encoder *sti_encoder = to_sti_tvout_encoder(encoder); 463 464 drm_encoder_cleanup(encoder); 465 kfree(sti_encoder); 466} 467 468static const struct drm_encoder_funcs sti_tvout_encoder_funcs = { 469 .destroy = sti_tvout_encoder_destroy, 470}; 471 472static void sti_dvo_encoder_commit(struct drm_encoder *encoder) 473{ 474 struct sti_tvout *tvout = to_sti_tvout(encoder); 475 476 tvout_dvo_start(tvout, sti_drm_crtc_is_main(encoder->crtc)); 477} 478 479static void sti_dvo_encoder_disable(struct drm_encoder *encoder) 480{ 481 struct sti_tvout *tvout = to_sti_tvout(encoder); 482 483 /* Reset VIP register */ 484 tvout_write(tvout, 0x0, TVO_VIP_DVO); 485} 486 487static const struct drm_encoder_helper_funcs sti_dvo_encoder_helper_funcs = { 488 .dpms = sti_tvout_encoder_dpms, 489 .mode_fixup = sti_tvout_encoder_mode_fixup, 490 .mode_set = sti_tvout_encoder_mode_set, 491 .prepare = sti_tvout_encoder_prepare, 492 .commit = sti_dvo_encoder_commit, 493 .disable = sti_dvo_encoder_disable, 494}; 495 496static struct drm_encoder * 497sti_tvout_create_dvo_encoder(struct drm_device *dev, 498 struct sti_tvout *tvout) 499{ 500 struct sti_tvout_encoder *encoder; 501 struct drm_encoder *drm_encoder; 502 503 encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); 504 if (!encoder) 505 return NULL; 506 507 encoder->tvout = tvout; 508 509 drm_encoder = (struct drm_encoder *)encoder; 510 511 drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; 512 drm_encoder->possible_clones = 1 << 0; 513 514 drm_encoder_init(dev, drm_encoder, 515 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_LVDS); 516 517 drm_encoder_helper_add(drm_encoder, &sti_dvo_encoder_helper_funcs); 518 519 return drm_encoder; 520} 521 522static void sti_hda_encoder_commit(struct drm_encoder *encoder) 523{ 524 struct sti_tvout *tvout = to_sti_tvout(encoder); 525 526 tvout_hda_start(tvout, sti_drm_crtc_is_main(encoder->crtc)); 527} 528 529static void sti_hda_encoder_disable(struct drm_encoder *encoder) 530{ 531 struct sti_tvout *tvout = to_sti_tvout(encoder); 532 533 /* reset VIP register */ 534 tvout_write(tvout, 0x0, TVO_VIP_HDF); 535 536 /* power down HD DAC */ 537 tvout_write(tvout, 1, TVO_HD_DAC_CFG_OFF); 538} 539 540static const struct drm_encoder_helper_funcs sti_hda_encoder_helper_funcs = { 541 .dpms = sti_tvout_encoder_dpms, 542 .mode_fixup = sti_tvout_encoder_mode_fixup, 543 .mode_set = sti_tvout_encoder_mode_set, 544 .prepare = sti_tvout_encoder_prepare, 545 .commit = sti_hda_encoder_commit, 546 .disable = sti_hda_encoder_disable, 547}; 548 549static struct drm_encoder *sti_tvout_create_hda_encoder(struct drm_device *dev, 550 struct sti_tvout *tvout) 551{ 552 struct sti_tvout_encoder *encoder; 553 struct drm_encoder *drm_encoder; 554 555 encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); 556 if (!encoder) 557 return NULL; 558 559 encoder->tvout = tvout; 560 561 drm_encoder = (struct drm_encoder *) encoder; 562 563 drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; 564 drm_encoder->possible_clones = 1 << 0; 565 566 drm_encoder_init(dev, drm_encoder, 567 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_DAC); 568 569 drm_encoder_helper_add(drm_encoder, &sti_hda_encoder_helper_funcs); 570 571 return drm_encoder; 572} 573 574static void sti_hdmi_encoder_commit(struct drm_encoder *encoder) 575{ 576 struct sti_tvout *tvout = to_sti_tvout(encoder); 577 578 tvout_hdmi_start(tvout, sti_drm_crtc_is_main(encoder->crtc)); 579} 580 581static void sti_hdmi_encoder_disable(struct drm_encoder *encoder) 582{ 583 struct sti_tvout *tvout = to_sti_tvout(encoder); 584 585 /* reset VIP register */ 586 tvout_write(tvout, 0x0, TVO_VIP_HDMI); 587} 588 589static const struct drm_encoder_helper_funcs sti_hdmi_encoder_helper_funcs = { 590 .dpms = sti_tvout_encoder_dpms, 591 .mode_fixup = sti_tvout_encoder_mode_fixup, 592 .mode_set = sti_tvout_encoder_mode_set, 593 .prepare = sti_tvout_encoder_prepare, 594 .commit = sti_hdmi_encoder_commit, 595 .disable = sti_hdmi_encoder_disable, 596}; 597 598static struct drm_encoder *sti_tvout_create_hdmi_encoder(struct drm_device *dev, 599 struct sti_tvout *tvout) 600{ 601 struct sti_tvout_encoder *encoder; 602 struct drm_encoder *drm_encoder; 603 604 encoder = devm_kzalloc(tvout->dev, sizeof(*encoder), GFP_KERNEL); 605 if (!encoder) 606 return NULL; 607 608 encoder->tvout = tvout; 609 610 drm_encoder = (struct drm_encoder *) encoder; 611 612 drm_encoder->possible_crtcs = ENCODER_CRTC_MASK; 613 drm_encoder->possible_clones = 1 << 1; 614 615 drm_encoder_init(dev, drm_encoder, 616 &sti_tvout_encoder_funcs, DRM_MODE_ENCODER_TMDS); 617 618 drm_encoder_helper_add(drm_encoder, &sti_hdmi_encoder_helper_funcs); 619 620 return drm_encoder; 621} 622 623static void sti_tvout_create_encoders(struct drm_device *dev, 624 struct sti_tvout *tvout) 625{ 626 tvout->hdmi = sti_tvout_create_hdmi_encoder(dev, tvout); 627 tvout->hda = sti_tvout_create_hda_encoder(dev, tvout); 628 tvout->dvo = sti_tvout_create_dvo_encoder(dev, tvout); 629} 630 631static void sti_tvout_destroy_encoders(struct sti_tvout *tvout) 632{ 633 if (tvout->hdmi) 634 drm_encoder_cleanup(tvout->hdmi); 635 tvout->hdmi = NULL; 636 637 if (tvout->hda) 638 drm_encoder_cleanup(tvout->hda); 639 tvout->hda = NULL; 640} 641 642static int sti_tvout_bind(struct device *dev, struct device *master, void *data) 643{ 644 struct sti_tvout *tvout = dev_get_drvdata(dev); 645 struct drm_device *drm_dev = data; 646 unsigned int i; 647 int ret; 648 649 tvout->drm_dev = drm_dev; 650 651 /* set preformatter matrix */ 652 for (i = 0; i < 8; i++) { 653 tvout_write(tvout, rgb_to_ycbcr_601[i], 654 TVO_CSC_MAIN_M0 + (i * 4)); 655 tvout_write(tvout, rgb_to_ycbcr_601[i], 656 TVO_CSC_AUX_M0 + (i * 4)); 657 } 658 659 sti_tvout_create_encoders(drm_dev, tvout); 660 661 ret = component_bind_all(dev, drm_dev); 662 if (ret) 663 sti_tvout_destroy_encoders(tvout); 664 665 return ret; 666} 667 668static void sti_tvout_unbind(struct device *dev, struct device *master, 669 void *data) 670{ 671 /* do nothing */ 672} 673 674static const struct component_ops sti_tvout_ops = { 675 .bind = sti_tvout_bind, 676 .unbind = sti_tvout_unbind, 677}; 678 679static int compare_of(struct device *dev, void *data) 680{ 681 return dev->of_node == data; 682} 683 684static int sti_tvout_master_bind(struct device *dev) 685{ 686 return 0; 687} 688 689static void sti_tvout_master_unbind(struct device *dev) 690{ 691 /* do nothing */ 692} 693 694static const struct component_master_ops sti_tvout_master_ops = { 695 .bind = sti_tvout_master_bind, 696 .unbind = sti_tvout_master_unbind, 697}; 698 699static int sti_tvout_probe(struct platform_device *pdev) 700{ 701 struct device *dev = &pdev->dev; 702 struct device_node *node = dev->of_node; 703 struct sti_tvout *tvout; 704 struct resource *res; 705 struct device_node *child_np; 706 struct component_match *match = NULL; 707 708 DRM_INFO("%s\n", __func__); 709 710 if (!node) 711 return -ENODEV; 712 713 tvout = devm_kzalloc(dev, sizeof(*tvout), GFP_KERNEL); 714 if (!tvout) 715 return -ENOMEM; 716 717 tvout->dev = dev; 718 719 /* get Memory ressources */ 720 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tvout-reg"); 721 if (!res) { 722 DRM_ERROR("Invalid glue resource\n"); 723 return -ENOMEM; 724 } 725 tvout->regs = devm_ioremap_nocache(dev, res->start, resource_size(res)); 726 if (!tvout->regs) 727 return -ENOMEM; 728 729 /* get reset resources */ 730 tvout->reset = devm_reset_control_get(dev, "tvout"); 731 /* take tvout out of reset */ 732 if (!IS_ERR(tvout->reset)) 733 reset_control_deassert(tvout->reset); 734 735 platform_set_drvdata(pdev, tvout); 736 737 of_platform_populate(node, NULL, NULL, dev); 738 739 child_np = of_get_next_available_child(node, NULL); 740 741 while (child_np) { 742 component_match_add(dev, &match, compare_of, child_np); 743 of_node_put(child_np); 744 child_np = of_get_next_available_child(node, child_np); 745 } 746 747 component_master_add_with_match(dev, &sti_tvout_master_ops, match); 748 749 return component_add(dev, &sti_tvout_ops); 750} 751 752static int sti_tvout_remove(struct platform_device *pdev) 753{ 754 component_master_del(&pdev->dev, &sti_tvout_master_ops); 755 component_del(&pdev->dev, &sti_tvout_ops); 756 return 0; 757} 758 759static const struct of_device_id tvout_of_match[] = { 760 { .compatible = "st,stih416-tvout", }, 761 { .compatible = "st,stih407-tvout", }, 762 { /* end node */ } 763}; 764MODULE_DEVICE_TABLE(of, tvout_of_match); 765 766struct platform_driver sti_tvout_driver = { 767 .driver = { 768 .name = "sti-tvout", 769 .owner = THIS_MODULE, 770 .of_match_table = tvout_of_match, 771 }, 772 .probe = sti_tvout_probe, 773 .remove = sti_tvout_remove, 774}; 775 776module_platform_driver(sti_tvout_driver); 777 778MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); 779MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); 780MODULE_LICENSE("GPL"); 781