linux/drivers/gpu/drm/i2c/ch7006_drv.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2009 Francisco Jerez.
   3 * All Rights Reserved.
   4 *
   5 * Permission is hereby granted, free of charge, to any person obtaining
   6 * a copy of this software and associated documentation files (the
   7 * "Software"), to deal in the Software without restriction, including
   8 * without limitation the rights to use, copy, modify, merge, publish,
   9 * distribute, sublicense, and/or sell copies of the Software, and to
  10 * permit persons to whom the Software is furnished to do so, subject to
  11 * the following conditions:
  12 *
  13 * The above copyright notice and this permission notice (including the
  14 * next paragraph) shall be included in all copies or substantial
  15 * portions of the Software.
  16 *
  17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
  21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  24 *
  25 */
  26
  27#include <linux/module.h>
  28
  29#include "ch7006_priv.h"
  30
  31/* DRM encoder functions */
  32
  33static void ch7006_encoder_set_config(struct drm_encoder *encoder,
  34                                      void *params)
  35{
  36        struct ch7006_priv *priv = to_ch7006_priv(encoder);
  37
  38        priv->params = *(struct ch7006_encoder_params *)params;
  39}
  40
  41static void ch7006_encoder_destroy(struct drm_encoder *encoder)
  42{
  43        struct ch7006_priv *priv = to_ch7006_priv(encoder);
  44
  45        drm_property_destroy(encoder->dev, priv->scale_property);
  46
  47        kfree(priv);
  48        to_encoder_slave(encoder)->slave_priv = NULL;
  49
  50        drm_i2c_encoder_destroy(encoder);
  51}
  52
  53static void  ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
  54{
  55        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
  56        struct ch7006_priv *priv = to_ch7006_priv(encoder);
  57        struct ch7006_state *state = &priv->state;
  58
  59        ch7006_dbg(client, "\n");
  60
  61        if (mode == priv->last_dpms)
  62                return;
  63        priv->last_dpms = mode;
  64
  65        ch7006_setup_power_state(encoder);
  66
  67        ch7006_load_reg(client, state, CH7006_POWER);
  68}
  69
  70static void ch7006_encoder_save(struct drm_encoder *encoder)
  71{
  72        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
  73        struct ch7006_priv *priv = to_ch7006_priv(encoder);
  74
  75        ch7006_dbg(client, "\n");
  76
  77        ch7006_state_save(client, &priv->saved_state);
  78}
  79
  80static void ch7006_encoder_restore(struct drm_encoder *encoder)
  81{
  82        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
  83        struct ch7006_priv *priv = to_ch7006_priv(encoder);
  84
  85        ch7006_dbg(client, "\n");
  86
  87        ch7006_state_load(client, &priv->saved_state);
  88}
  89
  90static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
  91                                      const struct drm_display_mode *mode,
  92                                      struct drm_display_mode *adjusted_mode)
  93{
  94        struct ch7006_priv *priv = to_ch7006_priv(encoder);
  95
  96        /* The ch7006 is painfully picky with the input timings so no
  97         * custom modes for now... */
  98
  99        priv->mode = ch7006_lookup_mode(encoder, mode);
 100
 101        return !!priv->mode;
 102}
 103
 104static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
 105                                     struct drm_display_mode *mode)
 106{
 107        if (ch7006_lookup_mode(encoder, mode))
 108                return MODE_OK;
 109        else
 110                return MODE_BAD;
 111}
 112
 113static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
 114                                     struct drm_display_mode *drm_mode,
 115                                     struct drm_display_mode *adjusted_mode)
 116{
 117        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
 118        struct ch7006_priv *priv = to_ch7006_priv(encoder);
 119        struct ch7006_encoder_params *params = &priv->params;
 120        struct ch7006_state *state = &priv->state;
 121        uint8_t *regs = state->regs;
 122        const struct ch7006_mode *mode = priv->mode;
 123        const struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
 124        int start_active;
 125
 126        ch7006_dbg(client, "\n");
 127
 128        regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
 129        regs[CH7006_BWIDTH] = 0;
 130        regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
 131                                         params->input_format);
 132
 133        regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
 134                | bitf(CH7006_CLKMODE_XCM, params->xcm)
 135                | bitf(CH7006_CLKMODE_PCM, params->pcm);
 136        if (params->clock_mode)
 137                regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
 138        if (params->clock_edge)
 139                regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
 140
 141        start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
 142        regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
 143        regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
 144
 145        regs[CH7006_INPUT_SYNC] = 0;
 146        if (params->sync_direction)
 147                regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
 148        if (params->sync_encoding)
 149                regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
 150        if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
 151                regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
 152        if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
 153                regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
 154
 155        regs[CH7006_DETECT] = 0;
 156        regs[CH7006_BCLKOUT] = 0;
 157
 158        regs[CH7006_SUBC_INC3] = 0;
 159        if (params->pout_level)
 160                regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
 161
 162        regs[CH7006_SUBC_INC4] = 0;
 163        if (params->active_detect)
 164                regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
 165
 166        regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
 167
 168        ch7006_setup_levels(encoder);
 169        ch7006_setup_subcarrier(encoder);
 170        ch7006_setup_pll(encoder);
 171        ch7006_setup_power_state(encoder);
 172        ch7006_setup_properties(encoder);
 173
 174        ch7006_state_load(client, state);
 175}
 176
 177static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
 178                                                       struct drm_connector *connector)
 179{
 180        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
 181        struct ch7006_priv *priv = to_ch7006_priv(encoder);
 182        struct ch7006_state *state = &priv->state;
 183        int det;
 184
 185        ch7006_dbg(client, "\n");
 186
 187        ch7006_save_reg(client, state, CH7006_DETECT);
 188        ch7006_save_reg(client, state, CH7006_POWER);
 189        ch7006_save_reg(client, state, CH7006_CLKMODE);
 190
 191        ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
 192                                           bitfs(CH7006_POWER_LEVEL, NORMAL));
 193        ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
 194
 195        ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
 196
 197        ch7006_write(client, CH7006_DETECT, 0);
 198
 199        det = ch7006_read(client, CH7006_DETECT);
 200
 201        ch7006_load_reg(client, state, CH7006_CLKMODE);
 202        ch7006_load_reg(client, state, CH7006_POWER);
 203        ch7006_load_reg(client, state, CH7006_DETECT);
 204
 205        if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
 206                    CH7006_DETECT_SVIDEO_C_TEST|
 207                    CH7006_DETECT_CVBS_TEST)) == 0)
 208                priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
 209        else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
 210                         CH7006_DETECT_SVIDEO_C_TEST)) == 0)
 211                priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
 212        else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
 213                priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
 214        else
 215                priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
 216
 217        drm_object_property_set_value(&connector->base,
 218                        encoder->dev->mode_config.tv_subconnector_property,
 219                                                        priv->subconnector);
 220
 221        return priv->subconnector ? connector_status_connected :
 222                                        connector_status_disconnected;
 223}
 224
 225static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
 226                                    struct drm_connector *connector)
 227{
 228        struct ch7006_priv *priv = to_ch7006_priv(encoder);
 229        const struct ch7006_mode *mode;
 230        int n = 0;
 231
 232        for (mode = ch7006_modes; mode->mode.clock; mode++) {
 233                if (~mode->valid_scales & 1<<priv->scale ||
 234                    ~mode->valid_norms & 1<<priv->norm)
 235                        continue;
 236
 237                drm_mode_probed_add(connector,
 238                                drm_mode_duplicate(encoder->dev, &mode->mode));
 239
 240                n++;
 241        }
 242
 243        return n;
 244}
 245
 246static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
 247                                           struct drm_connector *connector)
 248{
 249        struct ch7006_priv *priv = to_ch7006_priv(encoder);
 250        struct drm_device *dev = encoder->dev;
 251        struct drm_mode_config *conf = &dev->mode_config;
 252
 253        drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
 254
 255        priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2);
 256        if (!priv->scale_property)
 257                return -ENOMEM;
 258
 259        drm_object_attach_property(&connector->base, conf->tv_select_subconnector_property,
 260                                      priv->select_subconnector);
 261        drm_object_attach_property(&connector->base, conf->tv_subconnector_property,
 262                                      priv->subconnector);
 263        drm_object_attach_property(&connector->base, conf->tv_left_margin_property,
 264                                      priv->hmargin);
 265        drm_object_attach_property(&connector->base, conf->tv_bottom_margin_property,
 266                                      priv->vmargin);
 267        drm_object_attach_property(&connector->base, conf->tv_mode_property,
 268                                      priv->norm);
 269        drm_object_attach_property(&connector->base, conf->tv_brightness_property,
 270                                      priv->brightness);
 271        drm_object_attach_property(&connector->base, conf->tv_contrast_property,
 272                                      priv->contrast);
 273        drm_object_attach_property(&connector->base, conf->tv_flicker_reduction_property,
 274                                      priv->flicker);
 275        drm_object_attach_property(&connector->base, priv->scale_property,
 276                                      priv->scale);
 277
 278        return 0;
 279}
 280
 281static int ch7006_encoder_set_property(struct drm_encoder *encoder,
 282                                       struct drm_connector *connector,
 283                                       struct drm_property *property,
 284                                       uint64_t val)
 285{
 286        struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
 287        struct ch7006_priv *priv = to_ch7006_priv(encoder);
 288        struct ch7006_state *state = &priv->state;
 289        struct drm_mode_config *conf = &encoder->dev->mode_config;
 290        struct drm_crtc *crtc = encoder->crtc;
 291        bool modes_changed = false;
 292
 293        ch7006_dbg(client, "\n");
 294
 295        if (property == conf->tv_select_subconnector_property) {
 296                priv->select_subconnector = val;
 297
 298                ch7006_setup_power_state(encoder);
 299
 300                ch7006_load_reg(client, state, CH7006_POWER);
 301
 302        } else if (property == conf->tv_left_margin_property) {
 303                priv->hmargin = val;
 304
 305                ch7006_setup_properties(encoder);
 306
 307                ch7006_load_reg(client, state, CH7006_POV);
 308                ch7006_load_reg(client, state, CH7006_HPOS);
 309
 310        } else if (property == conf->tv_bottom_margin_property) {
 311                priv->vmargin = val;
 312
 313                ch7006_setup_properties(encoder);
 314
 315                ch7006_load_reg(client, state, CH7006_POV);
 316                ch7006_load_reg(client, state, CH7006_VPOS);
 317
 318        } else if (property == conf->tv_mode_property) {
 319                if (connector->dpms != DRM_MODE_DPMS_OFF)
 320                        return -EINVAL;
 321
 322                priv->norm = val;
 323
 324                modes_changed = true;
 325
 326        } else if (property == conf->tv_brightness_property) {
 327                priv->brightness = val;
 328
 329                ch7006_setup_levels(encoder);
 330
 331                ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
 332
 333        } else if (property == conf->tv_contrast_property) {
 334                priv->contrast = val;
 335
 336                ch7006_setup_properties(encoder);
 337
 338                ch7006_load_reg(client, state, CH7006_CONTRAST);
 339
 340        } else if (property == conf->tv_flicker_reduction_property) {
 341                priv->flicker = val;
 342
 343                ch7006_setup_properties(encoder);
 344
 345                ch7006_load_reg(client, state, CH7006_FFILTER);
 346
 347        } else if (property == priv->scale_property) {
 348                if (connector->dpms != DRM_MODE_DPMS_OFF)
 349                        return -EINVAL;
 350
 351                priv->scale = val;
 352
 353                modes_changed = true;
 354
 355        } else {
 356                return -EINVAL;
 357        }
 358
 359        if (modes_changed) {
 360                drm_helper_probe_single_connector_modes(connector, 0, 0);
 361
 362                if (crtc)
 363                        drm_crtc_helper_set_mode(crtc, &crtc->mode,
 364                                                 crtc->x, crtc->y,
 365                                                 crtc->primary->fb);
 366        }
 367
 368        return 0;
 369}
 370
 371static const struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
 372        .set_config = ch7006_encoder_set_config,
 373        .destroy = ch7006_encoder_destroy,
 374        .dpms = ch7006_encoder_dpms,
 375        .save = ch7006_encoder_save,
 376        .restore = ch7006_encoder_restore,
 377        .mode_fixup = ch7006_encoder_mode_fixup,
 378        .mode_valid = ch7006_encoder_mode_valid,
 379        .mode_set = ch7006_encoder_mode_set,
 380        .detect = ch7006_encoder_detect,
 381        .get_modes = ch7006_encoder_get_modes,
 382        .create_resources = ch7006_encoder_create_resources,
 383        .set_property = ch7006_encoder_set_property,
 384};
 385
 386
 387/* I2C driver functions */
 388
 389static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
 390{
 391        uint8_t addr = CH7006_VERSION_ID;
 392        uint8_t val;
 393        int ret;
 394
 395        ch7006_dbg(client, "\n");
 396
 397        ret = i2c_master_send(client, &addr, sizeof(addr));
 398        if (ret < 0)
 399                goto fail;
 400
 401        ret = i2c_master_recv(client, &val, sizeof(val));
 402        if (ret < 0)
 403                goto fail;
 404
 405        ch7006_info(client, "Detected version ID: %x\n", val);
 406
 407        /* I don't know what this is for, but otherwise I get no
 408         * signal.
 409         */
 410        ch7006_write(client, 0x3d, 0x0);
 411
 412        return 0;
 413
 414fail:
 415        ch7006_err(client, "Error %d reading version ID\n", ret);
 416
 417        return -ENODEV;
 418}
 419
 420static int ch7006_remove(struct i2c_client *client)
 421{
 422        ch7006_dbg(client, "\n");
 423
 424        return 0;
 425}
 426
 427static int ch7006_resume(struct device *dev)
 428{
 429        struct i2c_client *client = to_i2c_client(dev);
 430
 431        ch7006_dbg(client, "\n");
 432
 433        ch7006_write(client, 0x3d, 0x0);
 434
 435        return 0;
 436}
 437
 438static int ch7006_encoder_init(struct i2c_client *client,
 439                               struct drm_device *dev,
 440                               struct drm_encoder_slave *encoder)
 441{
 442        struct ch7006_priv *priv;
 443        int i;
 444
 445        ch7006_dbg(client, "\n");
 446
 447        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 448        if (!priv)
 449                return -ENOMEM;
 450
 451        encoder->slave_priv = priv;
 452        encoder->slave_funcs = &ch7006_encoder_funcs;
 453
 454        priv->norm = TV_NORM_PAL;
 455        priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
 456        priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
 457        priv->scale = 1;
 458        priv->contrast = 50;
 459        priv->brightness = 50;
 460        priv->flicker = 50;
 461        priv->hmargin = 50;
 462        priv->vmargin = 50;
 463        priv->last_dpms = -1;
 464        priv->chip_version = ch7006_read(client, CH7006_VERSION_ID);
 465
 466        if (ch7006_tv_norm) {
 467                for (i = 0; i < NUM_TV_NORMS; i++) {
 468                        if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
 469                                priv->norm = i;
 470                                break;
 471                        }
 472                }
 473
 474                if (i == NUM_TV_NORMS)
 475                        ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
 476                                   ch7006_tv_norm);
 477        }
 478
 479        if (ch7006_scale >= 0 && ch7006_scale <= 2)
 480                priv->scale = ch7006_scale;
 481        else
 482                ch7006_err(client, "Invalid scale setting \"%d\".\n",
 483                           ch7006_scale);
 484
 485        return 0;
 486}
 487
 488static const struct i2c_device_id ch7006_ids[] = {
 489        { "ch7006", 0 },
 490        { }
 491};
 492MODULE_DEVICE_TABLE(i2c, ch7006_ids);
 493
 494static const struct dev_pm_ops ch7006_pm_ops = {
 495        .resume = ch7006_resume,
 496};
 497
 498static struct drm_i2c_encoder_driver ch7006_driver = {
 499        .i2c_driver = {
 500                .probe = ch7006_probe,
 501                .remove = ch7006_remove,
 502
 503                .driver = {
 504                        .name = "ch7006",
 505                        .pm = &ch7006_pm_ops,
 506                },
 507
 508                .id_table = ch7006_ids,
 509        },
 510
 511        .encoder_init = ch7006_encoder_init,
 512};
 513
 514
 515/* Module initialization */
 516
 517static int __init ch7006_init(void)
 518{
 519        return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
 520}
 521
 522static void __exit ch7006_exit(void)
 523{
 524        drm_i2c_encoder_unregister(&ch7006_driver);
 525}
 526
 527int ch7006_debug;
 528module_param_named(debug, ch7006_debug, int, 0600);
 529MODULE_PARM_DESC(debug, "Enable debug output.");
 530
 531char *ch7006_tv_norm;
 532module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
 533MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
 534                 "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
 535                 "\t\tDefault: PAL");
 536
 537int ch7006_scale = 1;
 538module_param_named(scale, ch7006_scale, int, 0600);
 539MODULE_PARM_DESC(scale, "Default scale.\n"
 540                 "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
 541                 "\t\t\t1 -> Select default video modes.\n"
 542                 "\t\t\t2 -> Select video modes with a lower blanking ratio.");
 543
 544MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
 545MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
 546MODULE_LICENSE("GPL and additional rights");
 547
 548module_init(ch7006_init);
 549module_exit(ch7006_exit);
 550