drm/radeon/kms: add trivial debugging for voltage
[safe/jmp/linux-2.6] / drivers / ps3 / ps3av.c
index 21f69a7..a409fa0 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/delay.h>
 #include <linux/notifier.h>
 #include <linux/ioctl.h>
+#include <linux/fb.h>
+#include <linux/slab.h>
 
 #include <asm/firmware.h>
 #include <asm/ps3av.h>
@@ -33,6 +35,8 @@
 #define BUFSIZE          4096  /* vuart buf size */
 #define PS3AV_BUF_SIZE   512   /* max packet size */
 
+static int safe_mode;
+
 static int timeout = 5000;     /* in msec ( 5 sec ) */
 module_param(timeout, int, 0644);
 
@@ -56,8 +60,6 @@ static struct ps3av {
                struct ps3av_reply_hdr reply_hdr;
                u8 raw[PS3AV_BUF_SIZE];
        } recv_buf;
-       void (*flip_ctl)(int on, void *data);
-       void *flip_data;
 } *ps3av;
 
 /* color space */
@@ -75,23 +77,21 @@ static const struct avset_video_mode {
        u32 aspect;
        u32 x;
        u32 y;
-       u32 interlace;
-       u32 freq;
 } video_mode_table[] = {
        {     0, }, /* auto */
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I,       A_N,  720,  480, 1, 60},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P,       A_N,  720,  480, 0, 60},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ,  A_N, 1280,  720, 0, 60},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080, 1, 60},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080, 0, 60},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I,       A_N,  720,  576, 1, 50},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P,       A_N,  720,  576, 0, 50},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ,  A_N, 1280,  720, 0, 50},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080, 1, 50},
-       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080, 0, 50},
-       {  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA,       A_W, 1280,  768, 0, 60},
-       {  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA,       A_N, 1280, 1024, 0, 60},
-       {  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA,      A_W, 1920, 1200, 0, 60},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480I,       A_N,  720,  480},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_480P,       A_N,  720,  480},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_60HZ,  A_W, 1280,  720},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_60HZ, A_W, 1920, 1080},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_60HZ, A_W, 1920, 1080},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576I,       A_N,  720,  576},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_576P,       A_N,  720,  576},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_720P_50HZ,  A_W, 1280,  720},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080I_50HZ, A_W, 1920, 1080},
+       {YUV444, XRGB, PS3AV_CMD_VIDEO_VID_1080P_50HZ, A_W, 1920, 1080},
+       {  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WXGA,       A_W, 1280,  768},
+       {  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_SXGA,       A_N, 1280, 1024},
+       {  RGB8, XRGB, PS3AV_CMD_VIDEO_VID_WUXGA,      A_W, 1920, 1200},
 };
 
 /* supported CIDs */
@@ -533,7 +533,7 @@ static void ps3av_set_videomode_packet(u32 id)
        res = ps3av_cmd_avb_param(&avb_param, len);
        if (res == PS3AV_STATUS_NO_SYNC_HEAD)
                printk(KERN_WARNING
-                      "%s: Command failed. Please try your request again. \n",
+                      "%s: Command failed. Please try your request again.\n",
                       __func__);
        else if (res)
                dev_dbg(&ps3av->dev->core, "ps3av_cmd_avb_param failed\n");
@@ -541,7 +541,7 @@ static void ps3av_set_videomode_packet(u32 id)
 
 static void ps3av_set_videomode_cont(u32 id, u32 old_id)
 {
-       static int vesa = 0;
+       static int vesa;
        int res;
 
        /* video signal off */
@@ -551,9 +551,9 @@ static void ps3av_set_videomode_cont(u32 id, u32 old_id)
         * AV backend needs non-VESA mode setting at least one time
         * when VESA mode is used.
         */
-       if (vesa == 0 && (id & PS3AV_MODE_MASK) >= 11) {
+       if (vesa == 0 && (id & PS3AV_MODE_MASK) >= PS3AV_MODE_WXGA) {
                /* vesa mode */
-               ps3av_set_videomode_packet(2);  /* 480P */
+               ps3av_set_videomode_packet(PS3AV_MODE_480P);
        }
        vesa = 1;
 
@@ -585,54 +585,77 @@ static void ps3avd(struct work_struct *work)
        complete(&ps3av->done);
 }
 
-static int ps3av_resbit2id(u32 res_50, u32 res_60)
+#define SHIFT_50       0
+#define SHIFT_60       4
+#define SHIFT_VESA     8
+
+static const struct {
+       unsigned mask : 19;
+       unsigned id :  4;
+} ps3av_preferred_modes[] = {
+       { PS3AV_RESBIT_WUXGA      << SHIFT_VESA, PS3AV_MODE_WUXGA   },
+       { PS3AV_RESBIT_1920x1080P << SHIFT_60,   PS3AV_MODE_1080P60 },
+       { PS3AV_RESBIT_1920x1080P << SHIFT_50,   PS3AV_MODE_1080P50 },
+       { PS3AV_RESBIT_1920x1080I << SHIFT_60,   PS3AV_MODE_1080I60 },
+       { PS3AV_RESBIT_1920x1080I << SHIFT_50,   PS3AV_MODE_1080I50 },
+       { PS3AV_RESBIT_SXGA       << SHIFT_VESA, PS3AV_MODE_SXGA    },
+       { PS3AV_RESBIT_WXGA       << SHIFT_VESA, PS3AV_MODE_WXGA    },
+       { PS3AV_RESBIT_1280x720P  << SHIFT_60,   PS3AV_MODE_720P60  },
+       { PS3AV_RESBIT_1280x720P  << SHIFT_50,   PS3AV_MODE_720P50  },
+       { PS3AV_RESBIT_720x480P   << SHIFT_60,   PS3AV_MODE_480P    },
+       { PS3AV_RESBIT_720x576P   << SHIFT_50,   PS3AV_MODE_576P    },
+};
+
+static enum ps3av_mode_num ps3av_resbit2id(u32 res_50, u32 res_60,
+                                          u32 res_vesa)
 {
-       int id = 0;
-
-       if (res_50 > res_60) {  /* if res_50 == res_60, res_60 will be used */
-               if (res_50 & PS3AV_RESBIT_1920x1080P)
-                       id = 10;
-               else if (res_50 & PS3AV_RESBIT_1920x1080I)
-                       id = 9;
-               else if (res_50 & PS3AV_RESBIT_1280x720P)
-                       id = 8;
-               else if (res_50 & PS3AV_RESBIT_720x576P)
-                       id = 7;
-               else
-                       id = 0;
-       } else {
-               if (res_60 & PS3AV_RESBIT_1920x1080P)
-                       id = 5;
-               else if (res_60 & PS3AV_RESBIT_1920x1080I)
-                       id = 4;
-               else if (res_60 & PS3AV_RESBIT_1280x720P)
-                       id = 3;
-               else if (res_60 & PS3AV_RESBIT_720x480P)
-                       id = 2;
-               else
-                       id = 0;
-       }
-       return id;
+       unsigned int i;
+       u32 res_all;
+
+       /*
+        * We mask off the resolution bits we care about and combine the
+        * results in one bitfield, so make sure there's no overlap
+        */
+       BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 &
+                    PS3AV_RES_MASK_60 << SHIFT_60);
+       BUILD_BUG_ON(PS3AV_RES_MASK_50 << SHIFT_50 &
+                    PS3AV_RES_MASK_VESA << SHIFT_VESA);
+       BUILD_BUG_ON(PS3AV_RES_MASK_60 << SHIFT_60 &
+                    PS3AV_RES_MASK_VESA << SHIFT_VESA);
+       res_all = (res_50 & PS3AV_RES_MASK_50) << SHIFT_50 |
+                 (res_60 & PS3AV_RES_MASK_60) << SHIFT_60 |
+                 (res_vesa & PS3AV_RES_MASK_VESA) << SHIFT_VESA;
+
+       if (!res_all)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ps3av_preferred_modes); i++)
+               if (res_all & ps3av_preferred_modes[i].mask)
+                       return ps3av_preferred_modes[i].id;
+
+       return 0;
 }
 
-static int ps3av_hdmi_get_id(struct ps3av_info_monitor *info)
+static enum ps3av_mode_num ps3av_hdmi_get_id(struct ps3av_info_monitor *info)
 {
-       u32 res_50, res_60;
-       int id;
+       enum ps3av_mode_num id;
+
+       if (safe_mode)
+               return PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
 
        /* check native resolution */
-       res_50 = info->res_50.native & PS3AV_RES_MASK_50;
-       res_60 = info->res_60.native & PS3AV_RES_MASK_60;
-       if (res_50 || res_60) {
-               id = ps3av_resbit2id(res_50, res_60);
+       id = ps3av_resbit2id(info->res_50.native, info->res_60.native,
+                            info->res_vesa.native);
+       if (id) {
+               pr_debug("%s: Using native mode %d\n", __func__, id);
                return id;
        }
 
-       /* check resolution */
-       res_50 = info->res_50.res_bits & PS3AV_RES_MASK_50;
-       res_60 = info->res_60.res_bits & PS3AV_RES_MASK_60;
-       if (res_50 || res_60) {
-               id = ps3av_resbit2id(res_50, res_60);
+       /* check supported resolutions */
+       id = ps3av_resbit2id(info->res_50.res_bits, info->res_60.res_bits,
+                            info->res_vesa.res_bits);
+       if (id) {
+               pr_debug("%s: Using supported mode %d\n", __func__, id);
                return id;
        }
 
@@ -640,6 +663,7 @@ static int ps3av_hdmi_get_id(struct ps3av_info_monitor *info)
                id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
        else
                id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;
+       pr_debug("%s: Using default mode %d\n", __func__, id);
        return id;
 }
 
@@ -701,8 +725,42 @@ static void ps3av_monitor_info_dump(const struct ps3av_pkt_av_get_monitor_info *
        }
 }
 
-static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf,
-                               int boot)
+static const struct ps3av_monitor_quirk {
+       const char *monitor_name;
+       u32 clear_60;
+} ps3av_monitor_quirks[] = {
+       {
+               .monitor_name   = "DELL 2007WFP",
+               .clear_60       = PS3AV_RESBIT_1920x1080I
+       }, {
+               .monitor_name   = "L226WTQ",
+               .clear_60       = PS3AV_RESBIT_1920x1080I |
+                                 PS3AV_RESBIT_1920x1080P
+       }, {
+               .monitor_name   = "SyncMaster",
+               .clear_60       = PS3AV_RESBIT_1920x1080I
+       }
+};
+
+static void ps3av_fixup_monitor_info(struct ps3av_info_monitor *info)
+{
+       unsigned int i;
+       const struct ps3av_monitor_quirk *quirk;
+
+       for (i = 0; i < ARRAY_SIZE(ps3av_monitor_quirks); i++) {
+               quirk = &ps3av_monitor_quirks[i];
+               if (!strncmp(info->monitor_name, quirk->monitor_name,
+                            sizeof(info->monitor_name))) {
+                       pr_info("%s: Applying quirk for %s\n", __func__,
+                               quirk->monitor_name);
+                       info->res_60.res_bits &= ~quirk->clear_60;
+                       info->res_60.native &= ~quirk->clear_60;
+                       break;
+               }
+       }
+}
+
+static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf)
 {
        int i, res, id = 0, dvi = 0, rgb = 0;
        struct ps3av_pkt_av_get_monitor_info monitor_info;
@@ -719,6 +777,8 @@ static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf,
                ps3av_monitor_info_dump(&monitor_info);
 
                info = &monitor_info.info;
+               ps3av_fixup_monitor_info(info);
+
                switch (info->monitor_type) {
                case PS3AV_MONITOR_TYPE_DVI:
                        dvi = PS3AV_MODE_DVI;
@@ -737,28 +797,7 @@ static int ps3av_auto_videomode(struct ps3av_pkt_av_get_hw_conf *av_hw_conf,
                        id = PS3AV_DEFAULT_AVMULTI_MODE_ID_REG_50;
                if (ps3av->region & PS3AV_REGION_RGB)
                        rgb = PS3AV_MODE_RGB;
-       } else if (boot) {
-               /* HDMI: using DEFAULT HDMI_MODE_ID while booting up */
-               info = &monitor_info.info;
-               if (ps3av->region & PS3AV_REGION_60) {
-                       if (info->res_60.res_bits & PS3AV_RESBIT_720x480P)
-                               id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
-                       else if (info->res_50.res_bits & PS3AV_RESBIT_720x576P)
-                               id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;
-                       else {
-                               /* default */
-                               id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
-                       }
-               } else {
-                       if (info->res_50.res_bits & PS3AV_RESBIT_720x576P)
-                               id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;
-                       else if (info->res_60.res_bits & PS3AV_RESBIT_720x480P)
-                               id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_60;
-                       else {
-                               /* default */
-                               id = PS3AV_DEFAULT_HDMI_MODE_ID_REG_50;
-                       }
-               }
+               pr_debug("%s: Using avmulti mode %d\n", __func__, id);
        }
 
        return id | dvi | rgb;
@@ -800,7 +839,7 @@ static int ps3av_get_hw_conf(struct ps3av *ps3av)
 }
 
 /* set mode using id */
-int ps3av_set_video_mode(u32 id, int boot)
+int ps3av_set_video_mode(int id)
 {
        int size;
        u32 option;
@@ -813,8 +852,8 @@ int ps3av_set_video_mode(u32 id, int boot)
 
        /* auto mode */
        option = id & ~PS3AV_MODE_MASK;
-       if ((id & PS3AV_MODE_MASK) == 0) {
-               id = ps3av_auto_videomode(&ps3av->av_hw_conf, boot);
+       if ((id & PS3AV_MODE_MASK) == PS3AV_MODE_AUTO) {
+               id = ps3av_auto_videomode(&ps3av->av_hw_conf);
                if (id < 1) {
                        printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);
                        return -EINVAL;
@@ -834,34 +873,13 @@ int ps3av_set_video_mode(u32 id, int boot)
 
 EXPORT_SYMBOL_GPL(ps3av_set_video_mode);
 
-int ps3av_get_auto_mode(int boot)
+int ps3av_get_auto_mode(void)
 {
-       return ps3av_auto_videomode(&ps3av->av_hw_conf, boot);
+       return ps3av_auto_videomode(&ps3av->av_hw_conf);
 }
 
 EXPORT_SYMBOL_GPL(ps3av_get_auto_mode);
 
-int ps3av_set_mode(u32 id, int boot)
-{
-       int res;
-
-       res = ps3av_set_video_mode(id, boot);
-       if (res)
-               return res;
-
-       res = ps3av_set_audio_mode(PS3AV_CMD_AUDIO_NUM_OF_CH_2,
-                                  PS3AV_CMD_AUDIO_FS_48K,
-                                  PS3AV_CMD_AUDIO_WORD_BITS_16,
-                                  PS3AV_CMD_AUDIO_FORMAT_PCM,
-                                  PS3AV_CMD_AUDIO_SOURCE_SERIAL);
-       if (res)
-               return res;
-
-       return 0;
-}
-
-EXPORT_SYMBOL_GPL(ps3av_set_mode);
-
 int ps3av_get_mode(void)
 {
        return ps3av ? ps3av->ps3av_mode : 0;
@@ -869,36 +887,6 @@ int ps3av_get_mode(void)
 
 EXPORT_SYMBOL_GPL(ps3av_get_mode);
 
-int ps3av_get_scanmode(int id)
-{
-       int size;
-
-       id = id & PS3AV_MODE_MASK;
-       size = ARRAY_SIZE(video_mode_table);
-       if (id > size - 1 || id < 0) {
-               printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);
-               return -EINVAL;
-       }
-       return video_mode_table[id].interlace;
-}
-
-EXPORT_SYMBOL_GPL(ps3av_get_scanmode);
-
-int ps3av_get_refresh_rate(int id)
-{
-       int size;
-
-       id = id & PS3AV_MODE_MASK;
-       size = ARRAY_SIZE(video_mode_table);
-       if (id > size - 1 || id < 0) {
-               printk(KERN_ERR "%s: invalid mode %d\n", __func__, id);
-               return -EINVAL;
-       }
-       return video_mode_table[id].freq;
-}
-
-EXPORT_SYMBOL_GPL(ps3av_get_refresh_rate);
-
 /* get resolution by video_mode */
 int ps3av_video_mode2res(u32 id, u32 *xres, u32 *yres)
 {
@@ -926,6 +914,22 @@ int ps3av_video_mute(int mute)
 
 EXPORT_SYMBOL_GPL(ps3av_video_mute);
 
+/* mute analog output only */
+int ps3av_audio_mute_analog(int mute)
+{
+       int i, res;
+
+       for (i = 0; i < ps3av->av_hw_conf.num_of_avmulti; i++) {
+               res = ps3av_cmd_av_audio_mute(1,
+                       &ps3av->av_port[i + ps3av->av_hw_conf.num_of_hdmi],
+                       mute);
+               if (res < 0)
+                       return -1;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ps3av_audio_mute_analog);
+
 int ps3av_audio_mute(int mute)
 {
        return ps3av_set_audio_mute(mute ? PS3AV_CMD_MUTE_ON
@@ -934,28 +938,10 @@ int ps3av_audio_mute(int mute)
 
 EXPORT_SYMBOL_GPL(ps3av_audio_mute);
 
-void ps3av_register_flip_ctl(void (*flip_ctl)(int on, void *data),
-                            void *flip_data)
-{
-       mutex_lock(&ps3av->mutex);
-       ps3av->flip_ctl = flip_ctl;
-       ps3av->flip_data = flip_data;
-       mutex_unlock(&ps3av->mutex);
-}
-EXPORT_SYMBOL_GPL(ps3av_register_flip_ctl);
-
-void ps3av_flip_ctl(int on)
-{
-       mutex_lock(&ps3av->mutex);
-       if (ps3av->flip_ctl)
-               ps3av->flip_ctl(on, ps3av->flip_data);
-       mutex_unlock(&ps3av->mutex);
-}
-
-static int ps3av_probe(struct ps3_system_bus_device *dev)
+static int __devinit ps3av_probe(struct ps3_system_bus_device *dev)
 {
        int res;
-       u32 id;
+       int id;
 
        dev_dbg(&dev->core, " -> %s:%d\n", __func__, __LINE__);
        dev_dbg(&dev->core, "  timeout=%d\n", timeout);
@@ -970,15 +956,17 @@ static int ps3av_probe(struct ps3_system_bus_device *dev)
                return -ENOMEM;
 
        mutex_init(&ps3av->mutex);
-       ps3av->ps3av_mode = 0;
+       ps3av->ps3av_mode = PS3AV_MODE_AUTO;
        ps3av->dev = dev;
 
        INIT_WORK(&ps3av->work, ps3avd);
        init_completion(&ps3av->done);
        complete(&ps3av->done);
        ps3av->wq = create_singlethread_workqueue("ps3avd");
-       if (!ps3av->wq)
+       if (!ps3av->wq) {
+               res = -ENOMEM;
                goto fail;
+       }
 
        switch (ps3_os_area_get_av_multi_out()) {
        case PS3_PARAM_AV_MULTI_OUT_NTSC:
@@ -1003,7 +991,20 @@ static int ps3av_probe(struct ps3_system_bus_device *dev)
                       res);
 
        ps3av_get_hw_conf(ps3av);
-       id = ps3av_auto_videomode(&ps3av->av_hw_conf, 1);
+
+#ifdef CONFIG_FB
+       if (fb_mode_option && !strcmp(fb_mode_option, "safe"))
+               safe_mode = 1;
+#endif /* CONFIG_FB */
+       id = ps3av_auto_videomode(&ps3av->av_hw_conf);
+       if (id < 0) {
+               printk(KERN_ERR "%s: invalid id :%d\n", __func__, id);
+               res = -EINVAL;
+               goto fail;
+       }
+
+       safe_mode = 0;
+
        mutex_lock(&ps3av->mutex);
        ps3av->ps3av_mode = id;
        mutex_unlock(&ps3av->mutex);
@@ -1015,7 +1016,7 @@ static int ps3av_probe(struct ps3_system_bus_device *dev)
 fail:
        kfree(ps3av);
        ps3av = NULL;
-       return -ENOMEM;
+       return res;
 }
 
 static int ps3av_remove(struct ps3_system_bus_device *dev)
@@ -1048,7 +1049,7 @@ static struct ps3_vuart_port_driver ps3av_driver = {
        .shutdown = ps3av_shutdown,
 };
 
-static int ps3av_module_init(void)
+static int __init ps3av_module_init(void)
 {
        int error;